链接:PAT Advanced 1013
It is vitally important to have all the cities connected by highways in a war. If a city is occupied by the enemy, all the highways from/toward that city are closed. We must know immediately if we need to repair any other highways to keep the rest of the cities connected. Given the map of cities which have all the remaining highways marked, you are supposed to tell the number of highways need to be repaired, quickly.
For example, if we have 3 cities and 2 highways connecting city?1?? - city?2 and city?1?? - city?3 ?? . Then if city?1 ?? is occupied by the enemy, we must have 1 highway repaired, that is the highway city?2?? - city?3
Input Specification:
Each input file contains one test case. Each case starts with a line containing 3 numbers N (<1000), M and K, which are the total number of cities, the number of remaining highways, and the number of cities to be checked, respectively. Then M lines follow, each describes a highway by 2 integers, which are the numbers of the cities the highway connects. The cities are numbered from 1 to N. Finally there is a line containing K numbers, which represent the cities we concern.
Output Specification:
For each of the K cities, output in a line the number of highways need to be repaired if that city is lost.
Sample Input:
3 2 3
1 2
1 3
1 2 3
Sample Output:
1
0
0
题意:
给定一个无向图并规定,当删除图中某个结点时,将会把与之连接的边一起删除。接下来给出K个查询,每个查询给出一个欲删除的结点编号,求删除该结点(和与其相连接的边)后至少需要增加多少条边,才能使图变成连通。(注:K次查询均再原图上进行)
分析:
遍历删除结点后的图(不必真正去删除该结点,只要遍历时避开该结点即可),找出有多少个连通块,连通块数量-1 即 至少要增加的边数。
关于求连通块数量,有两种方法。
①DFS
每次都会遍历一个连通块,记录最终遍历了多少个即可。(用vis[max]标记已遍历过的结点,记得避开被删除的结点)
②并查集
让一个连通块的所有结点隶属于同一个集合,对一个集合有唯一的根结点。
所以要遍历删除结点后的图,构建出集合,然后再遍历一次,找出有多少个不同的根结点,即集合数量。(每次遍历都要避开被删除的结点)
以下代码:
①DFS
#include <bits/stdc++.h>
using namespace std;
const int maxn=1010;
vector<int> G[maxn]; //邻接表
int k;
bool vis[maxn];
int DFS(int x)
{
vis[x]=true;for(int i=0;i<G[x].size();i++){
if(!vis[G[x][i]]&&G[x][i]!=k)DFS(G[x][i]);}
}
int main()
{
int N,M,K;scanf("%d %d %d",&N,&M,&K);for(int i=0;i<M;i++){
int a,b;scanf("%d %d",&a,&b);G[a].push_back(b); //构建无向图G[b].push_back(a);}while(K--){
scanf("%d",&k); //k为被删除结点的编号memset(vis,false,sizeof(vis)); //记得每次都要重新初始化visint block=0; //连通块数量for(int i=1;i<=N;i++){
if(!vis[i]&&i!=k){
DFS(i);block++; }}printf("%d\n",block-1);}return 0;
}
②并查集
#include <bits/stdc++.h>
using namespace std;
const int maxn=1010;
vector<int> G[maxn]; //邻接表
int father[maxn]; //并查集
void init() //并查集的初始化
{
for(int i=0;i<maxn;i++)father[i]=i;
}
int find_father(int x) //查找x所在集合的根结点
{
int root=x;while(father[root]!=root)root=father[root];//路径压缩while(father[x]!=x){
int t=x;x=father[x];father[t]=root;}return root;
}
void Union(int a,int b) //合并集合
{
int Fa=find_father(a);int Fb=find_father(b);if(Fa!=Fb)father[Fa]=Fb;
}
int main()
{
int N,M,K;scanf("%d %d %d",&N,&M,&K);for(int i=0;i<M;i++){
int a,b;scanf("%d %d",&a,&b);G[a].push_back(b);G[b].push_back(a);}while(K--){
int k;set<int> root; //root存放不同集合的根结点scanf("%d",&k); //k为被删除结点的编号init(); //初始化并查集for(int i=1;i<=N;i++){
for(int j=0;j<G[i].size();j++){
if(i!=k&&G[i][j]!=k) //避开 kUnion(i,G[i][j]);}}for(int i=1;i<=N;i++){
if(i!=k) //这里也要避开 kroot.insert(find_father(i));}printf("%d\n",root.size()-1);}return 0;
}