题目链接:https://pintia.cn/problem-sets/994805046380707840/problems/994805063963230208
DFS求连通图
!!!注意一定要开exist数组来记录被摧毁的城市,被摧毁数组和访问数组是不一样的!!!
思路:就是对每个节点进行DFS,如果它存在于前面节点的DFS构成的连通图中或者别摧毁了就不会进行DFS,由此算出连通图的数量。每次摧毁一个城市,就一遍for循环,更新一下连通图的数量,和上次连通图的数量比较一下,再更新当前连通图的数量。
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e4+5;
int n; //城市的数量
bool vis[505],exist[505]; //访问数组,保存被摧毁的城市的数组
bool graph[505][505]; //记录城市之间的道路
void dfs(int cur)
{
vis[cur]=true; //标记被访问过
for(int i=0;i<n;i++)
{
if(!vis[i]&&graph[i][cur]&&!exist[cur]) //没有存在于其他的连通图中、存在边、没有被摧毁
dfs(i);
}
}
int main()
{
int m,u,v,k,q;
while(cin>>n>>m)
{
memset(vis,false,sizeof(vis));
memset(exist,false,sizeof(exist));
memset(graph,false,sizeof(graph));
while(m--)
{
cin>>u>>v;
graph[u][v]=graph[v][u]=true;
}
int num=0; //现有的连通图的数量
for(int i=0;i<n;i++) //遍历每一个节点进行深搜,寻找连通图
{
if(!vis[i]) //如果当前节点不存在于前面的连通图中
{
dfs(i);
num++; //连通图数量加一
}
}
cin>>k;
int newnum; //城市被摧毁后,当前连通图的数量
for(int j=0;j<k;j++)
{
cin>>q;
memset(vis,false,sizeof(vis));
exist[q]=true; //记录被摧毁的城市,因为每次vis都会清零
for(int i=0;i<n;i++) graph[i][q]=graph[q][i]=false; //将该城市的所有道路毁掉
newnum=0; //求出城市被摧毁后连通图的数量
for(int i=0;i<n;i++) //每个节点都遍历
{
if(!vis[i]&&!exist[i]) //如果该城市既没有存在于前面节点构成的连通图中,又没有被摧毁
{
dfs(i); //构建连通图
newnum++; //连通图数量加一
}
}
if(newnum>num) printf("Red Alert: City %d is lost!\n",q); //如果连通图数量增加了,则红色警报
else printf("City %d is lost.\n",q); //否则就是普通的城市丢失
num=newnum; //!!!一定要更新连通图的数量
}
if(k==n) printf("Game Over.\n");
}
return 0;
}
并查集求连通图
如果集合数相等或者前面完整结点的集合数+1=当前集合数,那么也就说明了去掉当前结点是没什么影响的,其余情况就是有影响哇
#include<bits/stdc++.h>
using namespace std;
struct edge //边
{
int u,v; //边连接的左右两个结点
};
int n; //城市的数量
edge e[5005]; //边的数组
int fat[505]; //并查集数组
bool vis[505]; //访问数组
void init() //初始化并查集
{
for(int i=0;i<=n;i++) fat[i]=i;
}
int finds(int x) //寻找根结点
{
return x==fat[x]?fat[x]:finds(fat[x]);
}
void Union(int x,int y) //合并两个结点
{
int xr=finds(x);
int yr=finds(y);
int temp;
while(xr!=yr)
{
temp=fat[xr];
fat[xr]=yr;
xr=temp;
}
}
int main()
{
int m,x,y,k,q,num,newnum;
while(cin>>n>>m)
{
init();
for(int i=0;i<m;i++)
{
cin>>x>>y;
e[i].u=x; e[i].v=y;
Union(e[i].u,e[i].v); //两个结点的合并,即连通图的建立
}
num=0;
for(int i=0;i<n;i++)
if(fat[i]==i) //求出连通图的数量
num++;
memset(vis,false,sizeof(vis)); //初始化访问数组
cin>>k;
for(int i=0;i<k;i++)
{
cin>>q;
vis[q]=true;
init();
newnum=0;
for(int j=0;j<m;j++) //遍历每一条边
{
if(vis[e[j].u]||vis[e[j].v]) //如果当前边的任意一个结点已在图中
continue;
else Union(e[j].u,e[j].v); //否则合并在一张图中
}
for(int j=0;j<n;j++)
if(fat[j]==j) //求出连通图的数量
newnum++;
if(newnum==num||newnum==num+1) printf("City %d is lost.\n",q);
else printf("Red Alert: City %d is lost!\n",q);
num=newnum; //更新连通图的数量
}
if(k==n) printf("Game Over.\n");
}
return 0;
}