1. 程式人生 > >Codeforces 776D.The Door Problem (dfs二分圖判定 / 並查集)

Codeforces 776D.The Door Problem (dfs二分圖判定 / 並查集)

二分圖染色 space find inline lin rem amp 屬於 兩個

題目鏈接:

http://codeforces.com/problemset/problem/776/D

題意:

n扇門,m個開關(n,m<=1e5),每個開關控制若幹個門,反轉開關門狀態變化,每個門正好被兩個開關控制,問是否有可能把所有門的狀態置為1?

思路:

from: http://blog.csdn.net/jeremy1149/article/details/56839453

方法一:二分圖染色

註意到一個門只被兩個開關控制 若門初始為0 則控制它的兩個開關狀態相反,門初始為1則 要求控制的兩個開關狀態相同,才能可以把門置為1.

以開關為頂點,(u,v)的邊為被(u,v)控制的門的初始狀態, dfs做二分圖染色,判斷每條邊(每扇門)是否都能被滿足

方法二:並查集

把每一個鑰匙拆成兩個點x,x+m,分別表示選不選這把鑰匙。
我們知道一扇門一定對應了兩把鑰匙。
設一扇門對應的要是分別為u,v,link(x,y)表示點x向點y連邊。
如果這扇門要操作一次,那就是兩把當中選一把:link(u,v+m),link(v,u+m)。這表示的是如果我選了拿第u把鑰匙就不能拿第v把鑰匙,如果我選了拿第v把鑰匙就不能拿第u把鑰匙
如果這扇門不要操作,那就是兩把當中選兩把或者都不選:link(u+m,v+m),link(v,u)。這表示的是如果我選了拿第u把鑰匙就一定要拿第v把鑰匙,如果我不拿第v把鑰匙就一定不能拿第u把鑰匙
這個是無向邊啊,並查集維護一下關系即可。
對於1...m中的每一把鑰匙,如果x,x+m屬於一個連通塊就無解了,因為包含關系成環。

代碼:

代碼一:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define MS(a) memset(a,0,sizeof(a))
#define MP make_pair
#define PB push_back
const int INF = 0x3f3f3f3f;
const ll INFLL = 0x3f3f3f3f3f3f3f3fLL;
inline ll read(){
    ll x=0,f=1;char ch=getchar();
    while(ch<0||ch>
9){if(ch==-)f=-1;ch=getchar();} while(ch>=0&&ch<=9){x=x*10+ch-0;ch=getchar();} return x*f; } ////////////////////////////////////////////////////////////////////////// const int maxn = 1e5+10; int n,k,st[maxn],c[maxn],flag; vector<int> p[maxn]; vector<pair<int,int> > g[maxn]; void dfs(int u,int x){ if(flag == false) return ; if(c[u] != -1){ if(c[u] != x) flag=false; return ; } c[u] = x; for(int i=0; i<(int)g[u].size(); i++){ int v = g[u][i].first, w = g[u][i].second; int y; if(w == 1) y = x; else y = x^1; dfs(v,y); } } int main(){ cin >> n >> k; for(int i=1; i<=n; i++) st[i] = read(); for(int i=1; i<=k; i++){ int num = read(); while(num--){ int x = read(); p[x].push_back(i); } } for(int i=1; i<=n; i++){ int u = p[i][0], v = p[i][1]; g[u].push_back(MP(v,st[i])); g[v].push_back(MP(u,st[i])); } flag = 1; memset(c,-1,sizeof(c)); for(int i=1; i<=k; i++){ if(c[i] == -1) dfs(i,0); } if(flag) puts("YES"); else puts("NO"); return 0; }

代碼二:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define MS(a) memset(a,0,sizeof(a))
#define MP make_pair
#define PB push_back
const int INF = 0x3f3f3f3f;
const ll INFLL = 0x3f3f3f3f3f3f3f3fLL;
inline ll read(){
    ll x=0,f=1;char ch=getchar();
    while(ch<0||ch>9){if(ch==-)f=-1;ch=getchar();}
    while(ch>=0&&ch<=9){x=x*10+ch-0;ch=getchar();}
    return x*f;
}
//////////////////////////////////////////////////////////////////////////
const int maxn = 2e5+10;

int n,k,st[maxn],fa[maxn];
vector<int> p[maxn];

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

void merge(int u,int v){
    int p1 = find(u), p2 = find(v);
    if(p1==p2) return ;
    fa[p1] = p2;
}

int main(){
    cin >> n >> k;
    for(int i=1; i<=n; i++)
        st[i] = read();
    for(int i=1; i<=k; i++){
        int num = read();
        for(int j=0; j<num; j++){
            int x = read();
            p[x].push_back(i);
        }
    }
    for(int i=1; i<=2*k; i++) fa[i]=i;
    for(int i=1; i<=n; i++){
        int u = p[i][0], v = p[i][1];
        if(st[i]){
            merge(u,v);
            merge(u+k,v+k);
        }else{
            merge(u,v+k);
            merge(u+k,v);
        }
    }
    for(int i=1; i<=k; i++){
        if(find(i) == find(i+k)){
            puts("NO");
            return 0;
        }
    }
    puts("YES");

    return 0;
}

Codeforces 776D.The Door Problem (dfs二分圖判定 / 並查集)