原图可能有多个连通分量,先DFS找出每个连通分量中最小节点,这些必然是要攻占的城市。
设 n 为节点数, m 为边数, cnt 为初始连通分量数,在剩下的边数不小于 m - (n - cnt) 的时候,图的连通性是不变的,也就是在这之前可以适当策略删边保持结果不变。
当边数小于等于 m - (n - cnt) 时,每删一条边,必然多一个连通分量,我们总可以做到让多出来这个连通分量的最小结点 是所有节点中除去已经选定的那些节点之外的最小节点,所以这时对节点以权值排序从小往大记到删够边数为止。
#include<stdio.h> #include<string.h> #include<stdlib.h> #include<algorithm> typedef long long LL; const int maxn = 1111111; const int maxm = 2111111; int n, m, k; int fst[maxn]; int wt[maxn]; int vis[maxn]; int nex[maxm], w[maxm], ntp; void AddEdge(int a, int b) { nex[ntp] = fst[a]; w[ntp] = b; fst[a] = ntp ++; } inline int min(int a, int b){return a < b ? a : b;} void DFS(int nd, int &okcity) { if(vis[nd]) return; vis[nd] = true; if(okcity == -1 || wt[nd] < wt[okcity]) okcity = nd; for(int i = fst[nd]; i != -1; i = nex[i]) DFS(w[i], okcity); } int main() { int t, ca; for(scanf("%d", &t), ca = 1; ca <= t; ca ++) { int a, b; scanf("%d%d%d", &n, &m, &k); memset(fst, -1, sizeof(fst)); memset(vis, 0, sizeof(vis)); for(int i = 1; i <= n; i ++) scanf("%d", &wt[i]); ntp = 0; for(int i = 0; i < m; i ++) { scanf("%d%d", &a, &b); AddEdge(a, b); AddEdge(b, a); } int cnt = 0; LL ans = 0; for(int i = 1; i <= n; i ++) { int okcity = -1; DFS(i, okcity); if(okcity != -1) cnt ++, ans += wt[okcity], vis[okcity] = 2; } if((k -= m - (n - cnt)) > 0) { int i, j; for(i = 1, j = 1; i <= n; i ++) if(vis[i] != 2) wt[j ++] = wt[i]; std::sort(wt + 1, wt + j); for(int i = 1; k > 0 && i <= j; i ++) ans += wt[i], k --; } printf("Case #%d: %lld\n", ca, ans); } return 0; }
时间: 2024-10-01 04:03:40