#1013. Battle Over Cities【DFS 遍历连通块 / 并查集】

原题链接

Problem Description:

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 c i t y 1 city_1 city1 - c i t y 2 city_2 city2 and c i t y 1 city_1 city1 - c i t y 3 city_3 city3. Then if c i t y 1 city_1 city1 is occupied by the enemy, we must have 1 highway repaired, that is the highway c i t y 2 city_2 city2 - c i t y 3 city_3 city3.

Input Specification:

Each input file contains one test case. Each case starts with a line containing 3 numbers N N N ( < 1000 <1000 <1000), M M M and K K K, which are the total number of cities, the number of remaining highways, and the number of cities to be checked, respectively. Then M M 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 N N. Finally there is a line containing K K K numbers, which represent the cities we concern.

Output Specification:

For each of the K K 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

Problem Analysis:

DFS 做法:

由于点数最多只有 1000 1000 1000,可以考虑使用 DFS。具体思路如下:

题目要求我们找到对于每个给定的查询城市,如果去掉这个城市,那么剩下的所有城市是否连通。

如果不连通的话,至少需要加几条边才可以使得全部连通。

这个可以用 DFS 遍历来做,除去查询城市以外,以每个其他没有被遍历过的城市为根进行遍历,看看遍历完所有城市一共需要几次,需要遍历几次就有几个连通块,连通块个数 -1 就是答案。但需要注意的是在以其他城市为根遍历过程中不可以再遍历到被去掉的那个城市。

这个做法在 PAT 上可以过,但是在 AcWing 上最后一个点超时了。

并查集做法:

不用邻接表存储图,直接将有边关系的两个点存入 pair<int, int> 中,方便并查集的处理。

每次询问,重新初始化并查集,对于每个 pair<int, int> 中的每对点,如果二者均不为被删去的城市 r,那么就可以相连,反之不可。

这样一开始除去城市 r 共有 n - 1 个城市,即 n - 1 个连通块,每合并一个少一个连通块,最后得到的连通块个数再 -1 就是答案。

Code

DFS
#include <iostream>
#include <algorithm>
#include <cstring>

using namespace std;

const int N = 1e3 + 10, M = N * N;

int n, m, k;
bool g[N][N];
int h[N], ne[M], e[M], idx;
bool st[N];

void add(int a, int b)
{
	e[idx] = b, ne[idx] = h[a], h[a] = idx ++ ;
}

int dfs(int u) // 返回能遍历到的点的个数
{
	st[u] = true;
	int cnt = 1;
	for (int i = h[u]; ~i; i = ne[i])
	{
		int j = e[i];
		if (!st[j]) cnt += dfs(j);
	}
	return cnt;
}

int main()
{
	cin >> n >> m >> k; // 节点编号 1 ~ n, k <= 1000
	memset(h, -1, sizeof h);
	for (int i = 0; i < m; i ++ )
	{
		int a, b;
		cin >> a >> b;
		add(a, b), add(b, a);
	}
	/*
		题目要求我们找到对于每个给定的查询城市,如果去掉这个城市,那么剩下的所有城市是否连通。
		如果不连通的话,至少需要加几条边才可以使得全部连通。

		这个可以用dfs遍历来做,除去查询城市以外,以每个其他没有被遍历过的城市为根进行遍历,看看遍历完所有城市一共需要几次,
		就有几个连通块,连通块个数-1就是答案。
	*/
	while (k -- )
	{
		int r;
		int cnt = 0, res = 0; // res当前已经遍历过的城市个数
		for (int i = 1; i <= n; i ++ ) st[i] = 0;
		cin >> r;
		st[r] = true;
		for (int i = 1; i <= n; i ++ )
			if (!st[i]) // 如果没有被遍历过 或者 不是查询城市r,就进行一次合法的dfs
			{
				res += dfs(i);
				++ cnt;
				if (res == n - 1) break;
			}
		cout << cnt - 1 << endl;
	}
	return 0;
}
并查集
#include <iostream>
#include <algorithm>
#include <cstring>
#include <vector>

using namespace std;

typedef pair<int, int> PII;

const int N = 1010;

int n, m, k;
int p[N];
vector<PII> city;

int find(int x)
{
	if (p[x] != x) p[x] = find(p[x]);
	return p[x];
}

int main()
{
	cin >> n >> m >> k;
	for (int i = 0; i < m; i ++ )
	{
		int a, b;
		scanf("%d%d", &a, &b);
		city.push_back({a, b});
	}
	
	while (k -- )
	{
		int r;
		cin >> r;
		for (int i = 1; i <= n; i ++ ) p[i] = i;

		int cnt = n - 1; // 除去被去掉的城市,一共还有n-1个点
		for (int i = 0; i < m; i ++ )
		{
			int a = city[i].first, b = city[i].second;
			if (a != r && b != r)
			{
				int pa = find(a), pb = find(b);
				if (pa != pb)
				{
					p[pa] = pb;
					cnt -- ; // 合并一个少一个连通块
				}
			}
		}
		cout << cnt - 1 << endl;
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值