zoukankan      html  css  js  c++  java
  • 并查集+思维——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 }
  • 相关阅读:
    向量积&&凸包算法
    K短路模板POJ 2449 Remmarguts' Date
    [USACO]奶牛抗议(DP+树状数组+离散化)
    [Uva1642]魔法Gcd(数论)
    [NOIP2012]疫情控制(二分答案+倍增+贪心)
    关于欧几里德算法(gcd)的证明
    旅行(LCA)
    [NOIP2015]运输计划(树上差分+LCA+二分)
    [USACO]奶牛博览会(DP)
    24.基于groovy脚本进行partial update
  • 原文地址:https://www.cnblogs.com/lfri/p/9485115.html
Copyright © 2011-2022 走看看