并查集《红色警报》

题目:

战争中保持各个城市间的连通性非常重要。本题要求你编写一个报警程序,当失去一个城市导致国家被分裂为多个无法连通的区域时,就发出红色警报。注意:若该国本来就不完全连通,是分裂的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

 



上一篇:ORA-00742:Log read detects lost writein thread 1 sequence 1202 block 137840


下一篇:git add 而未 commit 的文件丢失后找回