PTA L2-013 红色警报 题解

题目:
战争中保持各个城市间的连通性非常重要。本题要求你编写一个报警程序,当失去一个城市导致国家被分裂为多个无法连通的区域时,就发出红色警报。注意:若该国本来就不完全连通,是分裂的k个区域,而失去一个城市并不改变其他城市之间的连通性,则不要发出警报。

输入格式:
输入在第一行给出两个整数N(0 < N ≤ 500)和M(≤ 5000),分别为城市个数(于是默认城市从0到N-1编号)和连接两城市的通路条数。随后M行,每行给出一条通路所连接的两个城市的编号,其间以1个空格分隔。在城市信息之后给出被攻占的信息,即一个正整数K和随后的K个被攻占的城市的编号。

注意:输入保证给出的被攻占的城市编号都是合法的且无重复,但并不保证给出的通路没有重复。

输出格式:
对每个被攻占的城市,如果它会改变整个国家的连通性,则输出Red Alert: City k is lost!,其中k是该城市的编号;否则只输出City k is lost.即可。如果该国失去了最后一个城市,则增加一行输出Game Over.。

输入样例:
5 4
0 1
1 3
3 0
0 4
5
1 2 0 4 3
输出样例:
City 1 is lost.
City 2 is lost.
Red Alert: City 0 is lost!
City 4 is lost.
City 3 is lost.
Game Over.
代码长度限制
16 KB
时间限制
400 ms
内存限制
64 MB

 


处理技巧 :离线处理
思路:从后往前推,从已经有k个城市被占领到没有城市被占领。先将k个城市打上标记,然后求出全部被占领后剩下的城市构成几个连通块。再从第k个城市开始,将他设为没被摧毁,连通块总数加++,枚举他的所有邻点,如果没被摧毁,判断是否在一个连通块中,在则加入然后连通块,连通块总数--。全部枚举完后判断,如果连块总数减少则是“Red Alert”,否则不是,之后以此类推。

类似的题目有: [洛谷1197](https://www.luogu.com.cn/problem/P1197)

详细见代码

 

点击查看代码

#define _CRT_SECURE_NO_WARNINGS 1
#include<algorithm>
#include<fstream>
#include<iostream>
#include<cstdio>
#include<deque>
#include<string>
#include<stdlib.h>
#include<string.h>
#include<math.h>
#include<vector>
#include<stack>
#include<queue>
#include<map>
#include<set>
#include<bitset>
#include<unordered_map>
using namespace std;
#define INF 0x3f3f3f3f
#define MAXN 310000
#define N 200010
#define endl '\n'
#define exp 1e-8
#define lowbit(x) ((x)&amp;-(x))
const double pi = acos(-1.0);
typedef long long LL;
typedef unsigned long long ULL;
int s[N], n, m, k, arr[N], vis[N];  //arr[]存被摧毁的城市,vis[]给被摧毁的城市打标记,表示被摧毁,s[]存并查集
int cnt, dp[510][510];
bool flag = 0;
struct edge
{
	int v;
};
int ans[N];
vector<edge>e[N];  //存边(邻接表)
void init()        //初始化并查集
{
	for (int i = 0; i <= n; i++)
		s[i] = i;
}
int find(int x)   //路径压缩
{
	return s[x] = s[x] == x ? x : find(s[x]);
}
int main()
{
	cin  >> n >> m;
	init();
	for (int i = 1; i <= m; i++)
	{
		int a, b;
		cin >> a >> b;
		if (dp[a][b])continue;  //dp数组去重
		e[a].push_back({ b });
		e[b].push_back({ a });
		dp[a][b] = 1;
		dp[b][a] = 1;
	}
	cin >> k;
	for (int i = 1; i <= k; i++)
	{
		cin >> arr[i];
		vis[arr[i]] = 1;                //每个被毁坏的城市打个标记
	}                                     //这上面都是输入处理
	int mx = 0;
	for (int i = 0; i < n; i++)           //这一个for循环是将剩下没被摧毁的城市如果能加入一个连通块的话就加入;
	{
		if (!vis[i])
		{
			for (auto ed : e[i])
			{
				if (!vis[ed.v])
				{
					int x = find(i), y = find(ed.v);
					if (x != y)
					{
						s[x] = y;
					}
				}
			}
		}
	}
	for (int i = 0; i < n; i++)
	{
		if (!vis[i])
		{
			if (find(i) == i)
			{
				mx++;          //统计摧毁后有几个连通块
			}
		}
	}
	if (!mx)flag = 1;                //如果mx为0则表示全部城市被摧毁
	for (int i = k; i >= 1; i--)      //从k开始枚举
	{
		vis[arr[i]] = 0;           //设为没被摧毁
		cnt = mx + 1;              //连通块总数++
		for (auto ed : e[arr[i]])
		{
			if (!vis[ed.v])     //如果没被摧毁
			{
				int x = find(ed.v), y = find(arr[i]); //不在一个连通块中
				if (x != y)
				{
					s[x] = y;                      //合并
					cnt--;                         //连通块总数--
				}
			}
		}
		if (cnt >= mx)ans[i] = 0;   //跟之前设为被摧毁的连通块数量比较,如果大于等于之前,则是普通警告
		else  ans[i] = 1;           //不然则表示摧毁了这个城市连通块数量增多,则这个城市是枢纽,输出“Red Alert”;
		mx = cnt;                   //更新连通块总数
	}
	for (int i = 1; i <= k; i++)                //输出答案
	{
		if (ans[i])
			printf("Red Alert: City %d is lost!\n", arr[i]);
		else
			printf("City %d is lost.\n", arr[i]);
	}
	if (flag)
		printf("Game Over.\n");
	return 0;
}
posted @ 2023-03-15 23:27  魏老6  阅读(90)  评论(0编辑  收藏  举报