CF46F Hercule Poirot Problem 题解

题意:

有n个房间和m扇门,每扇门有且仅有一把钥匙
有k个人度过了两天,在第一天开始的时候所有的门都是关闭的,在第二天结束的时候,所有的门也都是关闭的
在这两天内,每个人可以执行如下操作若干次:
关上一扇门(前提:他有这扇门的钥匙,且这扇门与当前房间相连)
打开一扇门(前提:他有这扇门的钥匙,且这扇门与当前房间相连)
将自己手上的一些钥匙给与他同处于一间房里的其他人
走向一个与当前房间相连、且门是打开的房间
给出第一天开始前和第二天结束后每个人的位置及拥有的钥匙
判断是否可行

题解:
直接想不太好做。
转化一下:
可以发现,如果有解,一定可以把能打开的门都打开,在关回去。
而开和关是互逆的,所以,可以理解为两天都把能打开的门都打开。
如果都打开后,两天结果一样,则有解。
一样就是指打开的门相同,每个房间,人,钥匙所属集合分别相同。
这样就好做了。
暴力枚举能打开的边,用并查集维护每个集合的钥匙,暴力合并。
可以发现,枚举\(m\)次就够了。每次暴力合并复杂度为\(O(m)\),总时间复杂度为\(O(m^2)\)
代码:

#include <stdio.h> 
#include <map> 
using namespace std;
#define ull unsigned long long
#define se 13131 
int u[1005],v[1005],fa[1005],n,m,k;
bool zt[1005][1005];
int getv(int x) {
    if (x == fa[x]) return x;
    fa[x] = getv(fa[x]);
    return fa[x];
}
void merge(int x, int y) {
    x = getv(x);
    y = getv(y);
    if (x == y) return;
    if (x < y) {
        int t = x;
        x = y;
        y = t;
    }
    fa[x] = y;
    for (int i = 1; i <= m; i++) {
        if (zt[x][i]) zt[y][i] = true;
    }
}
ull ha(char zf[1005]) {
    ull rt = 0;
    for (int i = 0; zf[i] != 0; i++) rt = rt * se + zf[i];
    return rt;
}
map < ull,int > mp;
char zf[1005];
int wz[1005],rw[1005],yw[1005];
int w2[1005],r2[1005],y2[1005];
bool b1[1005],b2[1005];
int main() {
    scanf("%d%d%d", &n, &m, &k);
    for (int i = 1; i <= n; i++) fa[i] = i;
    for (int i = 1; i <= m; i++) scanf("%d%d", &u[i], &v[i]);
    for (int i = 1; i <= k; i++) {
        int a,s;
        scanf("%s%d%d", zf, &a, &s);
        rw[i] = a;
        mp[ha(zf)] = i;
        for (int j = 0; j < s; j++) {
            int b;
            scanf("%d", &b);
            zt[a][b] = 1;
            yw[b] = a;
        }
    }
    for (int a = 1; a <= m; a++) {
        for (int i = 1; i <= m; i++) {
            if (!b1[i] && (zt[getv(u[i])][i] || zt[getv(v[i])][i])) {
                merge(u[i], v[i]);
                b1[i] = true;
            }
        }
    }
    for (int i = 1; i <= n; i++) wz[i] = getv(i);
    for (int i = 1; i <= m; i++) yw[i] = getv(yw[i]);
    for (int i = 1; i <= k; i++) rw[i] = getv(rw[i]);
    for (int i = 1; i <= n; i++) {
        fa[i] = i;
        for (int j = 1; j <= m; j++) zt[i][j] = 0;
    }
    for (int i = 1; i <= k; i++) {
        int a,s;
        scanf("%s%d%d", zf, &a, &s);
        r2[mp[ha(zf)]] = a;
        for (int j = 0; j < s; j++) {
            int b;
            scanf("%d", &b);
            zt[a][b] = 1;
            y2[b] = a;
        }
    }
    for (int a = 1; a <= m; a++) {
        for (int i = 1; i <= m; i++) {
            if (!b2[i] && (zt[getv(u[i])][i] || zt[getv(v[i])][i])) {
                merge(u[i], v[i]);
                b2[i] = true;
            }
        }
    }
    for (int i = 1; i <= n; i++) w2[i] = getv(i);
    for (int i = 1; i <= m; i++) y2[i] = getv(y2[i]);
    for (int i = 1; i <= k; i++) r2[i] = getv(r2[i]);
    bool zd = false;
    for (int i = 1; i <= n; i++) {
        if (wz[i] != w2[i]) zd = true;
    }
    for (int i = 1; i <= n; i++) {
        if (rw[i] != r2[i]) zd = true;
    }
    for (int i = 1; i <= n; i++) {
        if (yw[i] != y2[i]) zd = true;
    }
    for (int i = 1; i <= m; i++) {
        if (b1[i] != b2[i]) zd = true;
    }
    if (zd) printf("NO");
    else printf("YES");
    return 0;
}

转载于:https://www.cnblogs.com/lnzwz/p/11342302.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值