团体天梯练习 L2-013 红色警报

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.


解题思路

简单概括一下题意,即处理多个询问,每个询问删除一个点,判断此时图中连通块的个数。如果连通块个数没有变,则输出 “City k is lost.”;如果删除某个点造成图中的连通性改变,则需要额外输出“Red Alert: ”。对于是否形成了 \(Game\) \(Over\) 的局面,因为题中说给出的多个城市序号合法且不重复,那么我们只需要在一开始判断处理询问的个数是否与图中点的个数相等即可。

一开始我没太理解题意,以为是判断图中的割点,我还一直心想PTA怎么会考这种比较偏的知识,然后写了个 \(Tarjan\) 算法判断割点,但是显然是错误的(大概只能拿18分),\(Tarjan\) 算法是一种离线的判断割点的方法,割点的定义是对于一无向连通图 \(G\) ,若去除 \(G\) 中的一个节点 \(u\) ,并删去与该节点相连的所有边后,\(G\) 变得不再连通(连通块数量增加),则称该点 \(u\) 为割点。但是题中的意思是在线判断每删除一个点,对 当前 图中连通性造成的影响。

所以,应当再没删除一个点与相关的边之后,重新建图,然后判断连通块数量。对于判断连通性,有 \(dfs\) 和 并查集两种方式,我个人比较喜欢用并查集。因为需要每次重新建图,所以所有的关系都要预先存储起来,然后每次询问时,与标记为删除的点相关的关系则不在加入到图中。

/*   一切都是命运石之门的选择  El Psy Kongroo  */
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<vector>
#include<queue>
#include<deque>
#include<stack>
#include<map>
#include<set>
#include<bitset>
#include<cmath>
#include<functional>
using namespace std;

typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
typedef pair<int, pii> piii;
typedef pair<double, double> pdd;
typedef pair<string, int> psi;
//typedef __int128 int128;
#define PI acos(-1.0)
#define x first
#define y second
//int dx[4] = {1, -1, 0, 0};
//int dy[4] = {0, 0, 1, -1};
const int inf = 0x3f3f3f3f, mod = 1e9 + 7;

const int N = 510;
int fa[N], n, m, k;
bool del[N];      //标记删除的点
vector<pii> edges;
int pre, after;   //之前连通块的数量 之后连通块的数量

int find(int x){
    return fa[x] == x ? x : (fa[x] = find(fa[x]));
}

void Union(int x, int y){
    int fx = find(x), fy = find(y);
    if(fx == fy) return;
    fa[fy] = fx;
}

int main(){
    ios::sync_with_stdio(false);
    cin.tie(0), cout.tie(0);

    cin >> n >> m;
    for(int i = 0; i < n; i ++ ) fa[i] = i;
    while(m -- ){
        int u, v; cin >> u >> v;
        edges.push_back({u, v});   //存储所有的关系
        Union(u, v);
    }

    for(int i = 0; i < n; i ++ ) 
        if(fa[i] == i) pre ++ ;   //现阶段连通块数量

    cin >> k;
    bool flag = (k == n);   //是否形成GameOver的局面
    while(k -- ){
        int x; cin >> x;
        del[x] = true;

        //重新建立并查集
        for(int i = 0; i < n; i ++ ) fa[i] = i;
        for(auto &[u, v] : edges){
            if(del[u] || del[v]) continue;
            Union(u, v);
        }

        after = 0;
        for(int i = 0; i < n; i ++ ) 
            if(fa[i] == i && !del[i]) after ++ ;   //之后连通块数量

        if(after > pre) cout << "Red Alert: City " << x << " is lost!" << endl;
        else cout << "City " << x << " is lost." << endl;

        pre = after;
    }

    if(flag) cout << "Game Over.";

    return 0;
}

posted @ 2023-04-17 16:08  MarisaMagic  阅读(21)  评论(0编辑  收藏  举报