1. 程式人生 > 實用技巧 >Aizu2224-Save your cats

Aizu2224-Save your cats

連結:https://vjudge.net/problem/Aizu-2224

題意:n為魔法樁的數量,m為圍欄的數量,第2到n+1行為每個魔法樁的座標,第n+2到m+1行表示兩個樁之間有圍欄,每個封閉的區域內都有至少一隻貓,需要的聖水數量與圍欄長度成正比,問救出所有貓需要破壞的圍欄長度。

分析:由題意可知要去掉一些邊,使得該圖變成樹,求這些邊的最小和。我們倒過來求該圖的最大生成樹,事先求出所有邊的權和,然後相減即可。

程式碼(Kruskal演算法):

 1 #include <iostream>
 2 #include <cstdio>
 3 #include <cmath>
 4
#include <algorithm> 5 using namespace std; 6 int n,m,f[11000],cnt; 7 double ans,k; 8 struct node 9 { 10 int x,y; 11 }p[11000]; 12 struct edge 13 { 14 int u,v; 15 double dis; 16 }e[110000]; 17 bool cmp(edge a,edge b) 18 { 19 return a.dis<b.dis; 20 } 21 int find(int k) 22 { 23 if
(f[k]==k)return k; 24 return f[k]=find(f[k]); 25 } 26 double powAnd(int i,int j) 27 { 28 return 1.0*((p[i].x-p[j].x)*(p[i].x-p[j].x)+(p[i].y-p[j].y)*(p[i].y-p[j].y)); 29 } 30 int main(void) 31 { 32 scanf("%d %d",&n,&m); 33 for(int i=1;i<=n;i++) 34 { 35 f[i]=i; 36
scanf("%d %d",&p[i].x,&p[i].y); 37 } 38 for(int i=1;i<=m;i++) 39 { 40 scanf("%d %d",&e[i].u,&e[i].v); 41 e[i].dis=-sqrt(powAnd(e[i].u,e[i].v));//因為要求最大生成樹 所以令邊權為相反數 42 ans=ans-e[i].dis; 43 } 44 sort(e+1,e+1+m,cmp); 45 for(int i=1;i<=m;i++) 46 { 47 int fu=find(e[i].u),fv=find(e[i].v); 48 if(fu==fv)continue; 49 f[fu]=fv; 50 k=k-e[i].dis; 51 if(++cnt==n-1)break; 52 } 53 printf("%f",ans-k); 54 return 0; 55 }