这道题的输出结果是如果那座城市失守,那么需要修复的公路数量。我们将连通的城市看出一个集合。那么需要修复的公路数量就是集合的数量-1(连通 n个点最少需要n-1)。而集合的合并以及确定集合的数量,可以使用并查集。
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
const int N=1010,M=500010;//N<1000,因为M是边,包含N个点最多的边的图为完全图即M<(N*(N-1)/2)
int n,m,k;
int p[N];//集合,p[i]表示i的祖宗(根节点)
struct Edge{
int a,b;
}e[M];//边,a,b为边的两个顶点
int find(int x){//压缩路径,返回x的祖宗。
if(x!=p[x]) p[x]=find(p[x]);
return p[x];
}
int main()
{
cin>>n>>m>>k;
for(int i=0;i<m;i++){//读入m条边
cin>>e[i].a>>e[i].b;
}
while(k--){
int x;
cin>>x;
int cnt=n-1;//去掉被占领的城市,共有n-1个城市,故有n-1个集合。
for(int i=1;i<=n;i++) p[i]=i;//并查集的初始化,一开始每个集合只有自己本身,i的祖宗即本身,故p[i]=i;
for(int i=0;i<m;i++){
int a=e[i].a,b=e[i].b;
if(a!=x&&b!=x){//该边与被占领的城市无关,则将两个顶点所属的集合合并
int pa=find(a),pb=find(b);//查找两个顶点的祖宗。
if(pa!=pb){//祖宗不相同
p[pa]=pb;//令(a的祖宗pa)的祖宗为pb,即两个集合合并
cnt--;//两集合合并,故集合数量-1
}
}
}
cout<<cnt-1<<endl;//剩下cnt个集合,需要cnt-1条路修复
}
return 0;
}
并查集的路径压缩:路径为压缩之前如图1,我们调用find(4),因为(4!=p[4]) 执行p[4]=find(p[4]);递归调用直到p[x]==x,由于p[1]=1,故返回1。则p[4]=1;此时变成图2,最终会变成图3。则下次查询祖宗时,效率会大大提升。
图1
图2
图3