并查集+思维——The Door Problem
Posted lfri
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了并查集+思维——The Door Problem相关的知识,希望对你有一定的参考价值。
一、问题描述(题目链接)
有n个门和m个开关,每个开关可以控制任意多的门,每个门严格的只有两个开关控制,问能否通过操作某些开关使得所有门都打开。(给出门的初始状态)。
二、问题分析
大部分开关问题首先要想到的一点就是任何开关操作两次以上都是无意义的,因此对于每个开关,我们要么操作一次,要么不操作。
因为已知门的初始状态,每个门由两个开关控制,所以我们可以调节这两个开关,使门保持打开的状态。
我们考虑用并查集维护开关之间的关系,因为每个开关有操作和不操作两种状态。,因此,我们要把每个点拆成两个点,i表示第i个开关没操作过,i+m表示第i个开关操作过。
若第i个门初始状态为1,控制开关为a和b,则合并a,b和a+m,b+m。
若初始状态为0,则合并a,b+m和b,a+m
最后判断i 和i+ m是否在同一集合,有任意一个,则不存在使门全部呈打开状态的操作。
三、代码实现
1 #include<stdio.h> 2 #include<iostream> 3 #include<algorithm> 4 #include<cstring> 5 #include<vector> 6 using namespace std; 7 8 const int maxn = 100000 + 10; 9 const int maxm = 100000 + 10; 10 int n, m, sta[maxn]; 11 int fa[2 * maxn]; 12 vector<int>door[maxn]; 13 14 void init() 15 { 16 for (int i = 0; i <= 2 * maxm; i++) 17 fa[i] = i; 18 } 19 20 int findset(int x) 21 { 22 if (x != fa[x]) 23 return fa[x] = findset(fa[x]); 24 return fa[x]; 25 } 26 27 void unite(int x, int y) 28 { 29 int rx = findset(x); 30 int ry = findset(y); 31 fa[rx] = ry; 32 } 33 34 int main() 35 { 36 int flag = 1; 37 init(); 38 scanf("%d%d", &n, &m); 39 for (int i = 1; i <= n; i++) 40 scanf("%d", &sta[i]); 41 for (int i = 1; i <= m; i++) 42 { 43 int cnt,tmp; 44 scanf("%d", &cnt); 45 while (cnt--) 46 { 47 scanf("%d", &tmp); 48 door[tmp].push_back(i); //输入的是每个开关控制的门,我们要建立的是每个门由哪些开关控制 49 } 50 } 51 52 for (int i = 1; i <= n; i++) 53 { 54 if (sta[i]) 55 { 56 unite(door[i][0], door[i][1]); 57 unite(door[i][0] + m, door[i][1] + m); 58 } 59 else 60 { 61 unite(door[i][0] + m, door[i][1]); 62 unite(door[i][0], door[i][1] + m); 63 } 64 } 65 for (int i = 1; i <= m; i++) 66 { 67 if (findset(i) == findset(i + m)) 68 { 69 flag = 0; 70 break; 71 } 72 } 73 printf("%s ", flag ? "YES" : "NO"); 74 return 0; 75 }
以上是关于并查集+思维——The Door Problem的主要内容,如果未能解决你的问题,请参考以下文章