当前位置: 代码迷 >> 综合 >> 天梯赛 红色警报(并查集)
  详细解决方案

天梯赛 红色警报(并查集)

热度:0   发布时间:2023-12-17 02:40:41.0

 

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

题目分析:

        0、本题中有个关键点是,每个要删除的点,是永久地从图中删掉了,后面再删除别的点时,前面已经删掉的点就不在图中了。所以要动态更新这个图的连通性。

        1、本题只考虑各个点之间的连通性,用并查集即可。而题中提到的连通性,即提示我们用并查集找出图中的连通块的个数。若删掉一个点后连通块数量增加,则说明这个点改变了图的连通性;若删掉后连通块数量不变,则说明他不改变连通性;若删掉后连通块数量-1,则说明这是个孤零零的城市,也不改变连通性。

        2、因为本题要动态更新图的信息,所以我们没必要在输入边后就建并查集,完全可以在删除点的时候再建。建的过程中,排除掉与要删除点相连的边。删掉点之后,更新当前连通块的个数,用于下次删除。

        3、如何确定哪些边要排除掉?可以把一个边的两个点存进两个数组from和to,一个存边的起点,一个存边的终点。且同一边的下标相同,这样就可以通过下标来读取一条边的两端点。在建并查集的时候,判断from和to是否有一个包含了要删除的点,就可以将其排除掉了。

        4、两个坑:不保证给出的通路没有重复,用一个边数组来储存输入过的边,重复输入后跳过即可;如果该国失去了最后一个城市,则增加一行输出,题中说保证要删除的点没有重复,即如果要删除的点的个数等于图中点的总个数,即game over了。

#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
int pre[501],edge[501][501],from[501],to[501],k=0,p,n,m,s,e;
bool vis[501];//删除后的点就不能访问了 
int find(int x)
{return x==pre[x]?x:pre[x]=find(pre[x]);
}
void unionf(int x,int y)
{int a=find(x),b=find(y);if(a!=b)pre[a]=b;
}
//建并查集、计算连通块个数 
int solve()
{for(int i=0;i<n;i++)pre[i]=i;for(int i=0;i<k;i++)if(!vis[from[i]] && !vis[to[i]])	//必须一条边的两端都不能包含删除了的点 unionf(from[i],to[i]);int count=0;for(int i=0;i<n;i++)if(i==pre[i] && !vis[i])count++;return count;
}
int main()
{cin>>n>>m;for(int i=0;i<m;i++){cin>>s>>e;if(!edge[s][e])		//忽略重复输入的边 {edge[s][e]=edge[e][s]=1;//把第i条边的两端存进from[i],to[i],这样可以通过i对边进行读取from[k]=s;	 to[k++]=e;}}int cnt=solve();cin>>p;for(int i=0;i<p;i++){cin>>s;//将该点的vis置为1,表示删除该点vis[s]=1;//重新建并查集 int temp=solve();if(temp>cnt)printf("Red Alert: City %d is lost!\n",s);elseprintf("City %d is lost.\n",s);//动态更新连通块数量 cnt=temp;}if(p==n)printf("Game Over.");return 0;
}