这个代码的意思是将路线图分块,能走通的节点合并为一块,记块的总数为num1。然后去掉一个节点后,再次分块,记块的总数为num2。如果num2-1(去掉了单独的一个节点)与num1相等或num2(去掉该节点之后,该节点所在的块仍互通)等于num1,则不影响连通性。
首先利用并查集计算出初始时连通块的数量。
然后攻掉一个城市,即去掉一个点,再次计算连通块的数量。
找到 去掉一个点时连通块数量变化的规律来设置判断是否要发出红色警报。
如果不理解的话,可以仔细看下代码:
//输入在第一行给出两个整数N(0 < N ≤ 500)和M(≤ 5000),
//分别为城市个数(于是默认城市从0到N-1编号)和连接两城市的通路条数。
//随后M行,每行给出一条通路所连接的两个城市的编号,其间以1个空格分隔。
//在城市信息之后给出被攻占的信息,即一个正整数K和随后的K个被攻占的城市的编号。
//注意:输入保证给出的被攻占的城市编号都是合法的且无重复,但并不保证给出的通路没有重复。
// 5 4
// 0 1
// 1 3
// 3 0
// 0 4
// 5
// 1 2 0 4 3
// 对每个被攻占的城市,如果它会改变整个国家的连通性,则输出Red Alert: City k is lost!,其中k是该城市的编号;
// 否则只输出City k is lost.即可。如果该国失去了最后一个城市,则增加一行输出Game Over.。
// City 1 is lost.
// City 2 is lost.
// Red Alert: City 0 is lost!
// City 4 is lost.
// City 3 is lost.
// Game Over.
#include <iostream>
#include <cstdio>
using namespace std;
int father[10010]={0};
int isRoot1[10010]={0};
int isRoot2[10010]={0};
int vis[10010]={0};
int num1=0;//完好无整的时候的联通个数//破坏相应点后的联通个数
struct Road{
int x,y;
}road[100010];
void init(int n){
for(int i=0;i<n;i++){
father[i]=i;
}
}
int findFather(int x){
if(x==father[x])return x;
else{
int F=findFather(father[x]);
father[x]=F;
return F;
}
}
void Union(int a,int b){
int faA=findFather(a);
int faB=findFather(b);
if(faA!=faB)
father[faA]=faB;
}
int main()
{
int n,m;
cin>>n>>m;
init(n);
for(int i=0;i<m;i++){//将2个有关系的点联系在一起
int a,b;
cin>>a>>b;
road[i].x=a;
road[i].y=b;
Union(a,b);
}
for(int i=0;i<n;i++){
if(findFather(i)!=0){//只有n个节点有父亲
isRoot1[father[i]]++;//在isRoot数组中,以根节点为下标的元素不为0,用来计算分组/联通个数
}
}
for(int i=0;i<n;i++){
if(isRoot1[i]!=0)
num1++;//完好无整的时候的联通个数
}
//cout<<"num1:"<<num1<<endl;
//==================================================
//计算//破坏相应点后的联通个数
int k;
cin>>k;
for(int i=0;i<k;i++){//要检验每一个点,计算失去这个点后的联通个数,与之前的比较,判断是否是红色警报
int lost;
cin>>lost;
init(n);//n个数全部初始化
vis[lost]=1;//标记该城市已经被攻破//不能再联通有该点的任何道路
for(int j=0;j<m;j++){//每个关系一一遍历---有没有被攻破的点
if(vis[road[j].x]==1||vis[road[j].y]==1){
continue;
}
//cout<<"仍然连接的节点"<<road[j].x<<"和"<<road[j].y;
Union(road[j].x,road[j].y);//合并没有删除的节点
}
// int num2=0;
// for(int i=0;i<n;i++){
// if(findFather(i)!=0){//只有n个节点有父亲
// isRoot2[father[i]]++;//在isRoot数组中,以根节点为下标的元素不为0,用来计算分组/联通个数
// }
// }
// for(int i=0;i<n;i++){
// if(isRoot2[i]!=0)
// num2++;//完好无整的时候的联通个数
// }
int num2 = 0;
for(int i=0;i<n;i++){
if(father[i] == i){
num2++;
}
}
if(num2-1 == num1 || num2 == num1){
cout<<"City "<<lost<<" is lost."<<endl;
}else{
cout<<"Red Alert: City "<<lost<<" is lost!"<<endl;
}
num1 = num2;
}
if(k>=n)cout<<"Game Over."<<endl;
return 0;
}
#include<iostream>
using namespace std;
int father[501];
int N,M;
int visit[501];
struct data{//记道路
int x;
int y;
};
int find(int x){
while(father[x] != x){
x = father[x];
}
return x;
}
void add(int x,int y){
int x1 = find(x);
int y1 = find(y);
if(x1 != y1){
father[x1] = y1;
}
}
int main(){
cin>>N>>M;
int a,b;
struct data node [M];
for(int i=0;i<N;i++){
father[i] = i;
}
for(int i=0;i<M;i++){
cin>>a>>b;
node[i].x = a;
node[i].y = b;
add(a,b);//合并节点,将可以连通的节点合并
}
int num1 = 0;
for(int i=0;i<N;i++){
if(father[i] == i){
num1++;//统计块的个数
}
}
int K;
cin>>K;int n;
for(int j=0;j<K;j++){
cin>>n;
//重新计算num
for(int i=0;i<N;i++){
father[i] = i;
}
visit[n] = 1;//这个数组的作用是,标记删除过的节点
for(int i=0;i<M;i++){
if(visit[node[i].x] == 1 || visit[node[i].y] == 1){
continue;//结束本次循环,进行下次循环
}
add(node[i].x,node[i].y);//合并没有被删除过的节点
}
int num2 = 0;
for(int i=0;i<N;i++){
if(father[i] == i){
num2++;
}
}
if(num2-1 == num1 || num2 == num1){
cout<<"City "<<n<<" is lost."<<endl;
}else{
cout<<"Red Alert: City "<<n<<" is lost!"<<endl;
}
num1 = num2;
}
if(K >= N)cout<<"Game Over."<<endl;
return 0;
}
//并查集 不好理解啊
#include<iostream>
#include<stdio.h>
#include<string.h>
using namespace std;
struct p{
int x;//代表路线的一端,“x”城市
int y;//代表路线的另一端,“y”城市
}eg[5001];//记录哪两个城市相连接,相当于路线两端点(存放两城市的连接关系)
int father[505];//主要数组 ,用来找根的关系
int find(int x)//查找根 ,
{
while(x != father[x])//一层一层往上找,直到找到根,递归的过程
{
x = father[x];//每找一层就是找"爸爸"的过程,直到找到"祖宗"
}
return x;
}
void uni(int x,int y)//“并”的过程
{
int x1 = find(x);//x的根,即祖先赋值给x1
int y1 = find(y);//y的根,即祖先赋值给y1
if(x1 != y1)//如果两个不属于同一个根,即两个人不是一个祖宗,比如一个姓朱,一个姓李
{ //姓朱的和姓李的不可能是一个祖先吧,但是这两家又产生了关系,比如联姻
//例如姓朱的男青年和姓李的黄花大闺女结婚了
//在古代的时候,”嫁鸡随鸡嫁狗随狗“女性是要跟随丈夫的姓氏,所以李姓闺女就要叫做”朱氏“
father[x1] = y1;
}
}
int main()
{
int n,m,u,v;
int vis[505];//标记数组
cin>>n>>m;//城市个数和路线条数
for(int j=0;j<n;j++)//初始化
{
father[j]=j;//初始化时每个人的根都是自己
}
for(int i=0;i<m;i++)
{
cin>>u>>v;//u、v之间有路
eg[i].x = u;
eg[i].y = v;//只要u,v有关系
uni(u,v);//连通的城市并起来(联姻的两家不就是一家人了吗)
}
int sum=0;//城市分几块(分为几个大家族)
for(int i=0;i<n;i++)
{
if(i == father[i])//如果根就是自己,很遗憾,没有黄花闺女看上你,称为"打光棍"
sum++;
}
int k,num;
cin>>k;//被攻占的个城市个数
memset(vis,0,sizeof(vis));//vis标记城市被攻占
for(int i=0;i<k;i++)
{
for(int j=0;j<n;j++)//输入被攻占的城市时,需要重新初始化
{
father[j]=j;//初始化时每个人的根都是自己
}
cin>>num;//输入被攻占的城市
vis[num]=1;//标记该市被攻占(离婚或者断绝父子关系)
for(int i=0;i<m;i++)
{
if(vis[eg[i].x] == 1 || vis[eg[i].y] == 1)//可以带入样例测试理解
continue;//如果被攻占的城市和其他暂时未被攻占的城市存在关系,此时随着该城市被攻占,相应的路线也随之消失,则不需要并
//continue指重新开始进行下一次循环;break是跳出该循环
uni(eg[i].x, eg[i].y);//否则就要并起来(未被攻占城市之间,本来有关系,的当然要并起来咯)
}
int cnt=0;
for(int i=0;i<n;i++)//看看现在城市分为几块
{
if(father[i] == i)//这一块,只剩自己并且自己就是自己的根 ,称之为“光杆司令 ”
cnt++;
}
if(cnt-1 == sum||cnt == sum)
//少了一个城市但这个城市并不影响整体连通性 (分为以下两种情况)
//即 1.要么少了一个城市 ,这个城市和其他城市本身是连接的
//该城市本来就是属于一大块中的一份子,少了它份数并不会少
// 2.要么少了一个城市 这个城市本身就是独立的一块,即光杆司令
//并不影响其他"大块"的连通性,少了它就会减少一块
printf("City %d is lost.\n",num);
else
printf("Red Alert: City %d is lost!\n", num);//否则不连通
sum=cnt;//更新块数
}
if(k >= n)
printf("Game Over.\n");
}