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)&-(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;
}