原图可能有多个连通分量,先DFS找出每个连通分量中最小节点,这些必然是要攻占的城市。
设 n 为节点数, m 为边数, cnt 为初始连通分量数,在剩下的边数不小于 m - (n - cnt) 的时候,图的连通性是不变的,也就是在这之前可以适当策略删边保持结果不变。
当边数小于等于 m - (n - cnt) 时,每删一条边,必然多一个连通分量,我们总可以做到让多出来这个连通分量的最小结点 是所有节点中除去已经选定的那些节点之外的最小节点,所以这时对节点以权值排序从小往大记到删够边数为止。
1 #include<stdio.h> 2 #include<string.h> 3 #include<stdlib.h> 4 #include<algorithm> 5 typedef long long LL; 6 const int maxn = 1111111; 7 const int maxm = 2111111; 8 int n, m, k; 9 int fst[maxn]; 10 int wt[maxn]; 11 int vis[maxn]; 12 int nex[maxm], w[maxm], ntp; 13 void AddEdge(int a, int b) 14 { 15 nex[ntp] = fst[a]; 16 w[ntp] = b; 17 fst[a] = ntp ++; 18 } 19 inline int min(int a, int b){return a < b ? a : b;} 20 void DFS(int nd, int &okcity) 21 { 22 if(vis[nd]) return; 23 vis[nd] = true; 24 if(okcity == -1 || wt[nd] < wt[okcity]) 25 okcity = nd; 26 for(int i = fst[nd]; i != -1; i = nex[i]) 27 DFS(w[i], okcity); 28 } 29 int main() 30 { 31 int t, ca; 32 for(scanf("%d", &t), ca = 1; ca <= t; ca ++) 33 { 34 int a, b; 35 scanf("%d%d%d", &n, &m, &k); 36 memset(fst, -1, sizeof(fst)); 37 memset(vis, 0, sizeof(vis)); 38 for(int i = 1; i <= n; i ++) 39 scanf("%d", &wt[i]); 40 ntp = 0; 41 for(int i = 0; i < m; i ++) 42 { 43 scanf("%d%d", &a, &b); 44 AddEdge(a, b); 45 AddEdge(b, a); 46 } 47 int cnt = 0; 48 LL ans = 0; 49 for(int i = 1; i <= n; i ++) 50 { 51 int okcity = -1; 52 DFS(i, okcity); 53 if(okcity != -1) 54 cnt ++, ans += wt[okcity], vis[okcity] = 2; 55 } 56 57 if((k -= m - (n - cnt)) > 0) 58 { 59 int i, j; 60 for(i = 1, j = 1; i <= n; i ++) 61 if(vis[i] != 2) wt[j ++] = wt[i]; 62 std::sort(wt + 1, wt + j); 63 for(int i = 1; k > 0 && i <= j; i ++) 64 ans += wt[i], k --; 65 } 66 printf("Case #%d: %lld ", ca, ans); 67 } 68 return 0; 69 }