并查集《红色警报》
作者:互联网
题目:
战争中保持各个城市间的连通性非常重要。本题要求你编写一个报警程序,当失去一个城市导致国家被分裂为多个无法连通的区域时,就发出红色警报。注意:若该国本来就不完全连通,是分裂的k个区域,而失去一个城市并不改变其他城市之间的连通性,则不要发出警报。
输入格式:
输入在第一行给出两个整数N
(0 < N
≤ 500)和M
(≤ 5000),分别为城市个数(于是默认城市从0到N
-1编号)和连接两城市的通路条数。随后M
行,每行给出一条通路所连接的两个城市的编号,其间以1个空格分隔。在城市信息之后给出被攻占的信息,即一个正整数K
和随后的K
个被攻占的城市的编号。
注意:输入保证给出的被攻占的城市编号都是合法的且无重复,但并不保证给出的通路没有重复。
输出格式:
对每个被攻占的城市,如果它会改变整个国家的连通性,则输出Red Alert: City k is lost!
,其中k
是该城市的编号;否则只输出City k is lost.
即可。如果该国失去了最后一个城市,则增加一行输出Game Over.
。
输入样例:
5 4
0 1
1 3
3 0
0 4
5
1 2 0 4 3
输出样例:
City 1 is lost.
City 2 is lost.
Red Alert: City 0 is lost!
City 4 is lost.
City 3 is lost.
Game Over.
①在判断是否改变连通性的时候,我第一个想法是每一个地点都找到pre,然后每次将两个地点加入并查集的时候都会不断更新pre使联通的地点的pre(即 爸爸 )都是相同的,最后我把新的pre数组和旧的pre数组进行比较看是否除了lost的城市之外有改变。
我当时pre数组的记录方式是,每个pre记录共同的“爸爸”,而“爸爸”的pre是自己本身。
这样出现的问题是,即使没有改变连通性每次lost一个城市重新用并查集得到的“爸爸”不一定相同。
②后来看了同学的代码,发现可以用 判断联通集合的个数 即爸爸的个数来判断是否联通。于是 我根据前后两次的“爸爸”数量是否相通来判断是否联通
但是,我设置的pre[爸爸]为爸爸本身,如果是一个本来就孤立的城市,那么查询的时候就会把它当成一个爸爸。但实际这个城市的存在与否不会影响到连通性。
③再后来我再看了同学代码,将pre[爸爸]改为-1,在数爸爸个数的时候就数-1的个数(此时-1的个数不再是连通块的个数了,但是可以根据-1个数的变化来判断是否改变了连通性),这样可以彻底不考虑那些孤立的城市。
但是如果是不孤立的尾端的城市lost了,结果就会多出一个-1,但是实际上也没有改变连通性。
④然后我就用一个变量death记录当前的lost城市,然后再数-1个数的时候跳过death。
但是也是因此-1的数量没有更新,而在下一次找爸爸数量的时候又将这个-1算了进来,因此错误。
⑤最后我又又看了同学代码。因为如果是尾端的城市lost,那么-1个数就会比原来多一个;如果是影响连通性的城市lost,因为改变连通性多一个“爸爸”, 所以多了一个-1, 而lost的城市的pre也变成-1,那么最后多出来的-1就有两个。
所以最后判断是否改变联通性的方式是 -1的个数是否与原来相等或者比原来多一个,如果是那么没有改变连通性,反之改变。
代码:
1 #include <iostream> 2 #include <fstream> 3 #include <cstring> 4 #include <algorithm> 5 6 using namespace std; 7 int N, M, areanum=-1; 8 const int mx=510; 9 int connection[mx][mx]; 10 int root[mx],pre[mx]; 11 12 int findroot(int vs); 13 int findset(); 14 void makefamily(int v1, int v2); 15 int main(){ 16 fill(connection[0], connection[0]+mx*mx, -1); 17 fill(pre, pre+mx, -1); 18 19 cin>>N>>M; 20 for(int i=1;i<=M;i++){ 21 int a, b; 22 cin>>a>>b; 23 connection[a][b]=1; 24 connection[b][a]=1; 25 makefamily(a, b); 26 } 27 findset(); 28 int K; 29 cin>>K; 30 int num=N; 31 for(int i=1;i<=K;i++){ 32 int death; 33 cin>>death; 34 for(int city=0;city<N;city++){ 35 if(connection[city][death]==1||connection[death][city]==1){ 36 connection[city][death]=-1; 37 connection[death][city]=-1; 38 } 39 } 40 fill(pre, pre+mx, -1); 41 42 for(int i=0;i<N;i++){ 43 for(int j=0;j<N;j++){ 44 if(connection[i][j]==1) 45 makefamily(i,j); 46 } 47 } 48 int flag=findset(); 49 50 (flag==-1)?printf("Red Alert: "):0; 51 printf("City %d is lost", death); 52 (flag==-1)?puts("!"):puts("."); 53 num--; 54 } 55 if(num==0) cout<<"Game Over."<<endl; 56 } 57 int findset(){ 58 int t=0, fake=areanum; 59 for(int i=0;i<N;i++){ 60 if(pre[i]==-1) t++; 61 } 62 areanum=t; 63 return (t==fake||t==fake+1)?1:-1;//判断是否联通 64 65 } 66 67 void makefamily(int v1, int v2){ 68 int t=v2; 69 v1=findroot(v1); 70 v2=findroot(v2); 71 if(v1!=v2){ 72 pre[v2]=v1; 73 } 74 } 75 int findroot(int vs){ 76 while(pre[vs]!=-1) vs=pre[vs]; 77 return vs; 78 }
后来百度了一下,发现还可以用dfs来做这道题hhh.
如https://blog.csdn.net/sgh666666/article/details/79752816
标签:pre,连通性,lost,int,城市,查集,个数,红色警报 来源: https://www.cnblogs.com/sloth612/p/12536725.html