并查集 - 红色警报 - 天梯赛 L2-013

并查集 - 红色警报 - 天梯赛 L2-013

题意:

给 定 一 个 n 个 点 m 条 边 的 无 向 图 , 给定一个n个点m条边的无向图, nm

接 着 进 行 k 次 操 作 , 每 次 去 除 图 中 的 一 个 点 , 判 断 这 个 点 是 否 是 该 点 所 在 连 通 块 的 割 点 。 接着进行k次操作,每次去除图中的一个点,判断这个点是否是该点所在连通块的割点。 k

输入:

首 行 两 个 正 整 数 n 和 m , 首行两个正整数n和m, nm

接 着 m 行 , 每 行 分 别 包 含 两 个 整 数 , 表 示 一 条 边 的 两 个 端 点 接着m行,每行分别包含两个整数,表示一条边的两个端点 m

然 后 一 个 正 整 数 k , 然后一个正整数k, k

最 后 一 行 为 k 个 整 数 , 按 照 次 序 移 除 每 个 点 。 最后一行为k个整数,按照次序移除每个点。 k

输出:

若 当 前 移 除 的 点 a i 不 是 该 点 所 在 连 通 块 的 割 点 , 则 输 出 : 若当前移除的点a_i不是该点所在连通块的割点,则输出: aiCity ai is lost.

否 则 输 出 : 否则输出: Red Alert: City ai is lost!

注意: 当 图 中 的 n 个 点 都 被 移 除 后 , 要 输 出 : 当图中的n个点都被移除后,要输出: nGame Over.

Game Over. 的 条 件 是 n 个 点 都 被 移 除 , 而 非 完 成 k 次 操 作 。 这 里 是 本 题 一 个 坑 点 。 的条件是n个点都被移除,而非完成k次操作。这里是本题一个坑点。 nk

输入样例:

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.

数据范围: N(0 < N ≤ 500)和M(≤ 5000),分别为城市个数(于是默认城市从0到N-1编号)和连接两城市的通路条数。


分析:

看 这 个 数 据 范 围 , 可 以 暴 力 ( 乱 搞 ) 看这个数据范围,可以暴力(乱搞) ()

如 果 一 个 点 是 割 点 , 那 么 把 这 个 点 独 立 出 来 之 后 , 整 个 图 中 连 通 块 的 数 量 至 少 增 加 2. 如果一个点是割点,那么把这个点独立出来之后,整个图中连通块的数量至少增加2. 2.

接 下 来 的 问 题 就 是 如 何 用 统 计 连 通 块 的 数 量 。 接下来的问题就是如何用统计连通块的数量。

利 用 并 查 集 统 计 连 通 块 的 数 量 , 判 断 条 件 : F i n d ( i ) = = i , 也 就 是 统 计 根 的 数 量 。 利用并查集统计连通块的数量,判断条件:Find(i)==i,也就是统计根的数量。 Find(i)==i

我 们 每 次 独 立 一 个 点 以 后 , 对 新 图 再 用 并 查 集 合 并 , 重 新 统 计 连 通 块 的 数 量 , 我们每次独立一个点以后,对新图再用并查集合并,重新统计连通块的数量,

然 后 用 上 一 次 统 计 的 连 通 块 数 量 c n t 与 本 次 统 计 的 连 通 块 数 量 c n t 1 作 差 , 判 断 。 然后用上一次统计的连通块数量cnt与本次统计的连通块数量cnt1作差,判断。 cntcnt1

可 以 用 一 个 b o o l 数 组 把 新 独 立 出 的 点 标 记 出 来 , 合 并 的 时 候 过 滤 到 包 含 这 些 点 的 边 。 可以用一个bool数组把新独立出的点标记出来,合并的时候过滤到包含这些点的边。 bool

代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<queue>
#include<cmath>
#include<set>
#include<map>
#include<vector>

#define P pair<int,int>
#define u first
#define v second
#define ll long long

using namespace std;

const int N = 510, M = 5010;

int n, m, k;
int p[N];
P E[M];
int cnt, cnt1;
bool st[N];

void Init()
{
    for(int i=1;i<=n;i++) p[i]=i;
}

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

void unite(int a,int b)
{
    a=Find(a), b=Find(b);
    p[a]=b;
}

int main()
{
    scanf("%d%d",&n,&m);
    Init();
    
    for(int i=0;i<m;i++)
    {
        int u,v;
        scanf("%d%d",&u,&v);
        u++, v++;
        E[i]={u,v};
        unite(u,v);
    }
    
    for(int i=1;i<=n;i++)
        if(Find(i)==i)
            cnt++;
    
    scanf("%d",&k);
    for(int i=1;i<=k;i++)
    {
        int t;
        scanf("%d",&t);
        t++;
        Init();
        st[t]=true;
        for(int j=0;j<m;j++)
        {
            int u=E[j].u, v=E[j].v;
            if(st[u]||st[v]) continue;
            unite(u,v);
        }
        
        for(int j=1;j<=n;j++)
            if(Find(j)==j)
                cnt1++;
                
        if(cnt1-cnt>1) printf("Red Alert: City %d is lost!\n",t-1);
        else printf("City %d is lost.\n",t-1);
        cnt=cnt1;
        cnt1=0;
        
        if(i==n)
        {
            puts("Game Over.");
            break;
        }
    }
    
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值