P1536 村村通
阿新 • • 發佈:2020-09-12
題目描述
某市調查城鎮交通狀況,得到現有城鎮道路統計表。表中列出了每條道路直接連通的城鎮。市政府 "村村通工程" 的目標是使全市任何兩個城鎮間都可以實現交通(但不一定有直接的道路相連,只要相互之間可達即可)。請你計算出最少還需要建設多少條道路?
輸入格式
輸入包含若干組測試測試資料,每組測試資料的第一行給出兩個用空格隔開的正整數,分別是城鎮數目nn和道路數目mm;隨後的mm行對應mm條道路,每行給出一對用空格隔開的正整數,分別是該條道路直接相連的兩個城鎮的編號。簡單起見,城鎮從11到nn編號。
注意:兩個城市間可以有多條道路相通。
輸出格式
對於每組資料,對應一行一個整數。表示最少還需要建設的道路數目。
輸入輸出樣例
輸入 #14 2 1 3 4 3 3 3 1 2 1 3 2 3 5 2 1 2 3 5 999 0 0
輸出 #1
1 0 2 998
程式碼:
#include<iostream> #include<cstdio> using namespace std; int n=1,m;//n代表有多少個村莊,m是代表已有多少條路 int f[10000];//儲存每個節點的“祖先” void init()//預處理,一開始把每個結點的祖先設為自己 { for(int i=1;i<=n;i++)f[i]=i; //千萬不能在輸入前用此函式(n都沒輸入怎麼預處理)} int find_(int x)//用到了路徑壓縮,比單純的while更快 { if(f[x]==x)//如果x的祖先是自己 retrun x;//那麼返回x就好了。 return find_(f[x]);//如果不是的話,x的祖先的祖先就是x的祖先 } void union_(int x,int y)//合併x,y兩個結點 { f[find_(y)]=find_(x);//讓x的祖先成為y的祖先的祖先,這樣y所在的集合裡所有結點的祖先全變為x的祖先。 } int main() { while(n!=0) { scanf("%d",&n);//輸入n if(n!=0)//判斷n是否等於0 scanf("%d",&m);//是就輸入m else//否則就跳過 continue; if(m==0)//如果m(路的條數)等於0的話,那麼想必就沒有路了 { printf("%d\n",n-1);//連起n個節點最少就要n-1條線 continue; } init();//預處理 int x,y;//定義x城鎮的y城鎮的編號 int sum=0;//用於儲存最少的路徑條數 int a[10000]={0};//桶,用於記錄有多少個沒有路徑的城鎮 for(int i=1;i<=m;i++) { scanf("%d%d",&x,&y);//輸入x and y的編號 union_(x,y);//因為x y之間有路線了,就合併它們 } for(int i=1;i<=n;i++)//開始判斷 { int c=find_(i);//判斷編號為c的城鎮的“祖先” if(!a[c])//如果這個“祖先”的編號沒有被記錄過的話 a[c]++,sum++;//那麼就記錄它,節點數加一。 } printf("%d\n",sum-1);//因為有n個不同的集合,那麼就至少要n-1條邊才能把它們連起來。 } return 0; }
#include <bits/stdc++.h> using namespace std; int pre[1000001],n,m,ans; inline int Find(int x){ return pre[x]==x?x:pre[x]=Find(pre[x]); } inline void Union(int x, int y){ int fx=Find(x),fy=Find(y); if(fx!=fy) pre[fx]=fy; } int main() { while(scanf("%d",&n)&&n){ ans=0; scanf("%d", &m); for(int i=1;i<=n;i++) pre[i]=i; for(int i=1,x,y;i<=m;i++){ scanf("%d%d",&x,&y); Union(x,y); } for(int i=1;i<=n;i++){ if(Find(i)==i) ans++; } printf("%d\n",ans-1); } return 0; }