L2-013 红色警报 并查集 逆向

题解

题目要求在当前城市被攻陷之后,有些城市会导致无法连通时发出红色警报。逆向思考,可以将删除操作改为添加。
如果添加当前点并且添加当前点连接的原有边后,导致原来两个不联通的部分连在一起则这个时候就是红色警报。
这个连接操作并且检测是否有两个不同的联通分量合并在一起,可以使用并查集压缩路径O(1)完成。

标记删除的点,逆向添加每个点,注意连接边时需要注意判断当前点是否存在
可以先和一个已存在的点建立连接在和其他的点连接并判断是否合并为同一个联通分量。总复杂度O(n+m)。

AC代码

#include <stdio.h>
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;

const int INF = 0x3f3f3f3f;
const ll LINF = 0x3f3f3f3f3f3f3f3f;
const int N = 510;
const int M = 5e3 + 10;
int los[M], vex[N]; //los攻陷顺序
bool red[M], vis[N]; //是否红色警报 是否被攻陷
vector<int> e[N];

int find(int x)
{
	return vex[x] == x ? x : vex[x] = find(vex[x]);
}
bool join(int x, int y)
{
	x = find(x), y = find(y);
	if (x != y)
	{
		vex[x] = y;
		return 1;
	}
	return 0;
}
int main()
{
#ifdef LOCAL
	freopen("C:/input.txt", "r", stdin);
#endif
	int n, m, p;
	cin >> n >> m;
	for (int i = 0; i < n; ++i)
		vex[i] = i;
	for (int i = 0; i < m; ++i)
	{
		int u, v;
		scanf("%d%d", &u, &v);
		e[u].push_back(v);
		e[v].push_back(u);
	}
	cin >> p;
	for (int i = 1; i <= p; ++i)
		scanf("%d", &los[i]), vis[los[i]] = 1;
	for (int i = 0; i < n; ++i)
		if (!vis[i])
			for (int j : e[i])
				if (!vis[j])
					join(i, j); //将最终都没被攻陷的城市join一起
	for (int i = p; i >= 1; --i)
	{
		int k = los[i]; //倒序攻陷点
		int j = 0;
		while (j < e[k].size() && vis[e[k][j]]) //找到第一个还存在的节点
			j++;
		if (j < e[k].size())
		{
			join(k, e[k][j++]); //先将第一个加入集合
			for (; j < e[k].size(); ++j)
				if (!vis[e[k][j]] && join(k, e[k][j])) //有其它存在的节点和现在的集合不一样
					red[k] = 1;
		}
		vis[k] = 0;
	}
	for (int i = 1; i <= p; ++i)
	{
		if (red[los[i]])
			printf("Red Alert: City %d is lost!\n", los[i]);
		else
			printf("City %d is lost.\n", los[i]);
	}
	if (p == n)
		printf("Game Over.\n");

	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值