这道题是最小生成树树的变形。题目告诉你在其中已经添加过了一些边,在剩下的边中选取若干条使其成为一个最小生成树。
我记得上一次做真题是遇到类似的问题。我用了prime算法求解。现在就换用了kruskal算法。
根据查询选把相应边合并,这些边不算入虽小生成树的权值即可,接下来的就和kruskal算法一致。
代码如下:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <cstdlib> 5 #include <cmath> 6 #include <algorithm> 7 #define INF 0x7fffffff 8 #define LEN 1010 9 using namespace std; 10 11 typedef struct{ 12 double x, y; 13 }POINT; 14 15 typedef struct { 16 int a, b; 17 double v; 18 }ARC; 19 20 int n, m, top, parent[LEN]; 21 POINT p[LEN]; 22 ARC Arc[LEN*LEN]; 23 double Map[LEN][LEN]; 24 25 inline double dis(POINT a, POINT b){return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));} 26 bool cmp(ARC a, ARC b){return a.v<b.v;} 27 //UFSet 28 void init() { for(int i=0; i<LEN; i++) parent[i] = i;} 29 int Find(int x){return parent[x]==x?x:parent[x]=Find(parent[x]);} 30 void Union(int a, int b){int pa = Find(a), pb = Find(b);if(pa!=pb)parent[pa] = pb;} 31 32 double solve() 33 { 34 int q, a, b, cnt = 0; 35 double ans = 0; 36 init(); 37 sort(Arc, Arc+top, cmp); 38 scanf("%d", &q); 39 for(int i=0; i<q; i++){ 40 scanf("%d%d", &a, &b); 41 if(Find(a)!=Find(b)){ 42 Union(a, b); 43 // ans += Map[a][b]; 44 cnt ++; 45 } 46 } 47 for(int i=0; i<top; i++){ 48 if(cnt==n-1)break; 49 if(Find(Arc[i].a)!=Find(Arc[i].b)){ 50 cnt ++; 51 Union(Arc[i].a, Arc[i].b); 52 ans += Arc[i].v; 53 } 54 } 55 return ans; 56 } 57 58 int main() 59 { 60 // freopen("in.txt", "r", stdin); 61 62 while(scanf("%d", &n)!=EOF) 63 { 64 for(int i=1; i<=n; i++){ 65 scanf("%lf%lf", &p[i].x, &p[i].y); 66 } 67 top = 0; 68 for(int i=1; i<=n ;i++){ 69 for(int j=i+1; j<=n; j++){ 70 Map[i][j] = Map[j][i] = dis(p[i], p[j]); 71 Arc[top].a = i; 72 Arc[top].b = j; 73 Arc[top].v = dis(p[i], p[j]); 74 top ++; 75 } 76 } 77 double ans = solve(); 78 printf("%.2lf ", ans); 79 } 80 return 0; 81 }