[机房练习赛4.3]airplane

Problem 4. airplane

Input file: airplane.in
Output file: airplane.out
Time limit: 1 second

cky 公司的运营蒸蒸日上,由于出差实在太频繁,而且坐汽车有些太慢了,所以cky 想要顺势直接进驻航空
业。cky 所在的国家天朝有n 个城市,m 个航空公司,每两个城市之间可能有一条航线运营(双向),一共
有k 条航线,每条航线都属于某一航空公司。现在cky 希望收购一家航空公司(为了涉足航空业以及防垄断,
cky 必须且只能购买一家航空公司),使得他能通过飞机从任意城市到达目的城市(允许转机),当然很可能没
有一家公司能满足cky 的要求,所以cky 还需收购其他公司掌控的航线。每个航空公司都有一个市值,每条
航线都有一个收购价。现在cky 想知道他最少需要花费多少钱。
Input
第1 行,3 个整数n; m; k,表示城市数量,航空公司数和航线数。城市用1; 2; : : : ; n 编号。
接下来一行,一共m 个整数,第i 个整数ai 表示第i 个航空公司的市值。接下来k 行,每行4 个整数
ui; vi; costi; bi,表示第i 条航线连接城市u; v,价值costi,所属航空公司为bi
题目保证u! = v
题目保证有解。
Output
输出最少需要花费多少钱
Sample
airplane.in
4 3 3
100 150 200
1 2 100 1
1 3 160 2
1 4 220 3
airplane.out
460
Note
• 对于50% 的数据,1 n 1000,1 m 1000,1 k 10000;
• 对于100% 的数据,1 n 2000,1 m 2000,1 k 200000,1 bi m,1 costi; ai 108。

最小生成树
“在最小生成树中任意一条边都是连接两个集合边权最小的一条边”
先跑一次MST,把得到的边记录下来,然后枚举航空公司

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
using namespace std;
typedef long long ll;
const int maxm = 200005;
const int maxn = 2005;
const long long INF = 1ll << 60;
struct line{
    int u,v;ll w;
    bool operator < (const line &b) const{return w < b.w;}
}l[maxm],mst[maxn];
struct edge{
    int u,v,next;
}e[maxm];
int h[maxn],num,fa[maxn];
int n,m,k,cnt;
ll a[maxn],ans;
void adde(int no,int u,int v){num++;e[num].u = u;e[num].v = v;e[num].next = h[no];h[no] = num;}
int getfather(int x){if(x == fa[x])return x;else return fa[x] = getfather(fa[x]);}
int main(){
    freopen("airplane.in","r",stdin);
    freopen("airplane.out","w",stdout);
    scanf("%d%d%d", &n, &m, &k);
    int u,v,w,kl;
    for( int i = 1; i <= m; i++ ) scanf("%I64d", &a[i]);
    for( int i = 1; i <= k; i++ ){
        scanf("%d%d%d%d", &u, &v, &w, &kl);
        l[i].u = u;l[i].v = v;l[i].w = w;adde(kl,u,v);
    }
    sort(l+1,l+k+1);
    for( int i = 1; i <= n; i++ ) fa[i] = i;
    for( int i = 1; i <= k; i++ ){
        int d1 = getfather(l[i].u);int d2 = getfather(l[i].v);
        if( d1 != d2 ) {fa[d2] = d1;mst[++cnt] = l[i];}
    }
    ans = INF;
    for( int i = 1; i <= m; i++ ){
        for( int j = 1; j <= n; j++ )fa[j] = j;
        for( int j = h[i]; j; j = e[j].next ){
            int d1 = getfather(e[j].u);
            int d2 = getfather(e[j].v);
            if( d1 != d2 ) fa[d2] = d1;
        }
        ll rs = a[i];
        for( int j = 1; j <= cnt; j++ ){
            int d1 = getfather(mst[j].u);
            int d2 = getfather(mst[j].v);
            if( d1 != d2 ){fa[d2] = d1;rs += mst[j].w;}
        }
        ans = min(ans,rs);
    }
    printf("%I64d",ans);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值