朋友之間的圈子數、暢通工程類似
題目描述:
某省調查城鎮交通狀況,得到現有城鎮道路統計表,表中列出了每條道路直接連通的城鎮。省政府“暢通工程”的目標是使全省任何兩個城鎮間都可以實現交通(但不一定有直接的道路相連,只要互相間接通過道路可達即可)。問最少還需要建設多少條道路?
Input
測試輸入包含若干測試用例。每個測試用例的第1行給出兩個正整數,分別是城鎮數目N ( < 1000 )和道路數目M;隨後的M行對應M條道路,每行給出一對正整數,分別是該條道路直接連通的兩個城鎮的編號。爲簡單起見,城鎮從1到N編號。當N爲0時,輸入結束,該用例不被處理。
Output
對每個測試用例,在1行裏輸出最少還需要建設的道路數目。
Sample Input
4 2 1 3 4 3 3 3 1 2 1 3 2 3 5 2 1 2 3 5 999 0 0
Sample Output
1 0 2 998
題意:
還需建多少條路能使所有村子兩兩相通,我們先把已經建好的道路的點合併,如果還需要建道路,那麼所有點組成的集合數量一定大於1。那麼我們只要查詢n個村子構成了多少個集合,即詢問一個點的父節點是否爲本身。最終的集合數-1即是解,爲啥減一,如集合:a b c,只需要連接a——b——c,兩條線即可使得集合互通,所以結果爲集合數減一
代碼:
#include<cstdio>
#include<iostream>
using namespace std;
int city[1010];
int find_father(int n) //查找祖先節點,當節點記錄的祖先是自己,則表示查找到祖先了
{
if(city[n] == n) {
return n;
}
return find_father(city[n]);
}
void merge_ab(int a,int b) //合併節點:設置共同祖先
{
int x = find_father(a);
int y = find_father(b);
if(x != y)
city[y] = x;
}
int main()
{
int n,m;
while(~scanf("%d",&n),n)
{
scanf("%d",&m);
for(int i = 0; i <= n; i++) //最開始的時候,每個節點都是自己的祖先
city[i] = i;
int a,b;
for(int i = 0; i < m; i++) {
scanf("%d%d",&a,&b);
merge_ab(a,b);
}
int res = 0;
for(int i = 1; i <= n; i++) { //n個村子構成的集合數
if(city[i] == i)
res++;
}
cout << res-1 << endl;
}
return 0;
}
小希的迷宮
題目描述:
上次Gardon的迷宮城堡小希玩了很久(見Problem B),現在她也想設計一個迷宮讓Gardon來走。但是她設計迷宮的思路不一樣,首先她認爲所有的通道都應該是雙向連通的,就是說如果有一個通道連通了房間A和B,那麼既可以通過它從房間A走到房間B,也可以通過它從房間B走到房間A,爲了提高難度,小希希望任意兩個房間有且僅有一條路徑可以相通(除非走了回頭路)。小希現在把她的設計圖給你,讓你幫忙判斷她的設計圖是否符合她的設計思路。比如下面的例子,前兩個是符合條件的,但是最後一個卻有兩種方法從5到達8。
Input
輸入包含多組數據,每組數據是一個以0 0結尾的整數對列表,表示了一條通道連接的兩個房間的編號。房間的編號至少爲1,且不超過100000。每兩組數據之間有一個空行。整個文件以兩個-1結尾。
Output
對於輸入的每一組數據,輸出僅包括一行。如果該迷宮符合小希的思路,那麼輸出"Yes",否則輸出"No"。
Sample Input
6 8 5 3 5 2 6 4
5 6 0 0
8 1 7 3 6 2 8 9 7 5
7 4 7 8 7 6 0 0
3 8 6 8 6 4
5 3 5 6 5 2 0 0
-1 -1
Sample Output
Yes
Yes
No
題意:
1.房間編號並不是1—n,有些數字並沒有出現,所以要用一個數組標記數字是否出現;
2.要判斷所有房間是不是屬於同一個集合。
代碼:
#include<cstdio>
#include<iostream>
using namespace std;
const int maxn = 100010;
int city[maxn];
int flag[maxn];
int find_father(int n)
{
if(n == city[n])
return n;
return find_father(city[n]);
}
bool merge_nm(int n, int m)
{
int x = find_father(n);
int y = find_father(m);
if(x == y)
return false;
city[x] = y;
return true;
}
int main()
{
int n,m;
while(~scanf("%d%d",&n,&m))
{
if(n == -1 && m == -1)
break;
for(int i = 0; i <= 100000; i++) {
city[i] = i;
flag[i] = 0;
}
bool is = merge_nm(n,m);
flag[n] = 1;
flag[m] = 1;
if(m == 0 && n == 0) //若一組數據的開頭即是0,0;輸出Yes
printf("Yes\n");
while(scanf("%d%d",&n,&m),n&&m)
{
if(merge_nm(n,m) == false) //何必,若返回false,表示有相同的父節點
is = false; //記錄一下狀態
flag[n] = 1;
flag[m] = 1;
}
if(is == false)
printf("No\n");
else {
int res = 0;
for(int i = 0; i <= 100000; i++) { //統計存在幾個集合
if(flag[i] && city[i] == i)
res++;
}
if(res == 1)
printf("Yes\n");
else
printf("No\n");
}
}
return 0;
}
more is better
題目描述:
給定n個直接的朋友關係,求一個最大的集合
使這個集合裏的所有人都直接或間接的滿足朋友關係
求這個集合的大小。
Input
輸入n (0 ≤ n ≤ 100 000) - 直接的朋友關係. 接下來n行,每行輸入兩個數A,B,A與B互爲朋友(A ≠ B, 1 ≤ A, B ≤ 10000000)
Output
集合大小的最大值
案例:
Sample Input
4
1 2
3 4
5 6
1 6
4
1 2
3 4
5 6
7 8Sample Output
4
2
題意:
跟上面的朋友圈問題類似,求出最大朋友圈然後在求出這個最大朋友圈元素的個數,A和B是朋友,B和C是朋友則A和C也是朋友,依次類推。這裏有個地方得注意,在找父節點時需要狀態壓縮,不然會超時
AC代碼:
#include<cstdio>
#include<iostream>
using namespace std;
const int maxn = 100001;
int fri[maxn];
int sum[maxn];
int res;
int find_fri(int n)
{
if(fri[n] == n)
return n;
return fri[n] = find_fri(fri[n]); //return find_fri(fri[n]);會出現超時,因此需要狀態跟着改變
}
void merge_ab(int a,int b)
{
int x = find_fri(a);
int y = find_fri(b);
if(x != y) {
fri[x] = y;
sum[y] += sum[x];
res = max(res,sum[y]);
}
}
int main()
{
int n;
while(~scanf("%d",&n))
{
for(int i = 0; i <= maxn; i++) {
fri[i] = i;
sum[i] = 1;
}
int a,b;
res = 1;
for(int i = 0; i < n; i++) {
scanf("%d%d",&a,&b);
merge_ab(a,b);
}
printf("%d\n",res);
}
return 0;
}