PAT Advanced 1013 Battle Over Cities(图,DFS/并查集)

链接: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));  //记得每次都要重新初始化vis
		int 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)        //避开 k
					Union(i,G[i][j]);
			}
		}
		for(int i=1;i<=N;i++)
		{
			if(i!=k)        //这里也要避开 k
				root.insert(find_father(i));
		}
		printf("%d\n",root.size()-1);
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值