• CF776D The Door Problem


    CF776D The Door Problem

    原题链接

    题意:

    给定 n扇门 m 把钥匙,每一把钥匙会同时控制 k_i 扇门,每扇门最多被两把钥匙控制。求是否存在一个使用钥匙的方法使得全部的门都变成开的。

    思路1(2-sat):

    与上题类似,考虑每扇门的不同初始状态造成的不同操作。

    若该门初始状态为0,即关时:两个钥匙的状态应该是(0,1)或(1,0);

    若该门初始状态为1,即开时:两个钥匙的状态应该是(0,0)或(1,1);

    考虑2-sat建图,注意要建双向边。

    代码1:

    #include<bits/stdc++.h>
    using namespace std;
    const int maxn=2e5+100;
    
    vector<int>g[maxn];
    int n,m,r[maxn];
    vector<int>a[maxn];
    
    void add(int u,int v){
    	g[u].push_back(v);
    	g[v].push_back(u);
    }
    
    int dfn[maxn],low[maxn],timetmp,id[maxn],cnt;
    bool instk[maxn];
    stack<int>stk;
    
    void tarjan(int u){
    	dfn[u]=low[u]=++timetmp;
    	stk.push(u);
    	instk[u]=1;
    	for(int t:g[u]){
    		if(!dfn[t]){
    			tarjan(t);
    			low[u]=min(low[u],low[t]);
    		}
    		else if(instk[t]) low[u]=min(low[u],dfn[t]);
    	}
    	if(low[u]==dfn[u]){
    		int y;cnt++;
    		do{
    			y=stk.top();
    			stk.pop();
    			instk[y]=0;
    			id[y]=cnt;
    		}while(y!=u);
    	}
    }
    
    int main(){
    	cin>>n>>m;
    	for(int i=1;i<=n;i++) cin>>r[i];
    	for(int i=1;i<=m;i++){
    		int x;cin>>x;
    		for(int j=1;j<=x;j++){
    			int t;cin>>t;
    			a[t].push_back(i);
    		}
    	}
    	for(int i=1;i<=n;i++){
    		int u=a[i][0],v=a[i][1];
    		if(r[i]){
    			add(u,v);add(u+m,v+m);
    		}
    		else{
    			add(u,v+m);add(u+m,v);
    		}
    	}
    	for(int i=1;i<=2*m;i++)
    		if(!dfn[i]) tarjan(i);
    	for(int i=1;i<=m;i++)
    		if(id[i]==id[i+m]){
    			puts("NO");return 0;
    		}
    	puts("YES");
    	return 0;
    }
    
    

    思路2(并查集):

    这题没有输出方案的话,可以不用那么复杂,只需要判断矛盾点是否在同一集合里就好了,用并查集维护,根据初始状态合并对应的集合,最后判断每对矛盾点是否在同一集合里。

    代码2:

    #include<bits/stdc++.h>
    using namespace std;
    const int maxn=2e5+100;
    
    int n,m,r[maxn];
    vector<int>a[maxn];
    int root[maxn];
    
    int Find(int x){
    	if(x!=root[x]) root[x]=Find(root[x]);
    	return root[x];
    } 
    
    void Union(int u,int v){
    	int fu=Find(u),fv=Find(v);
    	if(fu!=fv) root[fu]=fv;
    }
    
    int main(){
    	cin>>n>>m;
    	for(int i=1;i<=2*m;i++) root[i]=i;
    	for(int i=1;i<=n;i++) cin>>r[i];
    	for(int i=1;i<=m;i++){
    		int x;cin>>x;
    		for(int j=1;j<=x;j++){
    			int t;cin>>t;
    			a[t].push_back(i);
    		}
    	}
    	for(int i=1;i<=n;i++){
    		int u=a[i][0],v=a[i][1];
    		if(r[i]){
    			Union(u,v);Union(u+m,v+m);
    		}
    		else{
    			Union(u,v+m);Union(u+m,v);
    		}
    	}
    	for(int i=1;i<=m;i++)
    		if(Find(i)==Find(i+m)){
    			puts("NO");return 0;
    		}
    	puts("YES");
    	return 0;
    }
    
    
    
    
    
    
    
    
    
    
    

    思路3(染色法):

    有点类似二分图那个染色。能染色的原因还是因为每个钥匙只有用或不用两种选择。

    如果门的初始状态为0的话,两个钥匙染不同的颜色。

    反之,染相同的颜色。

    判断是否能够染色成功。

    代码3:

    #include<bits/stdc++.h>
    using namespace std;
    const int maxn=2e5+100;
    typedef pair<int,int>PII;
    
    int n,m,r[maxn];
    vector<int>a[maxn];
    vector<PII>g[maxn];
    int col[maxn];
    
    bool dfs(int u,int c){
    	if(col[u]){
    		if(col[u]!=c) return 0;
    		return 1;
    	}
    	col[u]=c;
    	for(int i=0;i<g[u].size();i++){
    		int v=g[u][i].first,w=g[u][i].second;
    		if(w==1){
    			if(!dfs(v,c)) return 0;
    		}
    		else{
    			if(!dfs(v,3-c)) return 0;
    		}
    	}
    	return 1;
    }
    
    int main(){
    	cin>>n>>m;
    	for(int i=1;i<=n;i++) cin>>r[i];
    	for(int i=1;i<=m;i++){
    		int x;cin>>x;
    		for(int j=1;j<=x;j++){
    			int t;cin>>t;
    			a[t].push_back(i);
    		}
    	}
    	for(int i=1;i<=n;i++){
    		int u=a[i][0],v=a[i][1];
    		g[u].push_back({v,r[i]});
    		g[v].push_back({u,r[i]});
    	}
    	for(int i=1;i<=m;i++){
    		if(!col[i]){
    			if(!dfs(i,1)){
    				puts("NO");return 0;
    			}
    		}
    	}
    	puts("YES");
    	return 0;
    }
    
    
    
    
    
    
    
    
    
    
    
  • 相关阅读:
    ASP.NET
    ASP.NET
    MSSQL
    ASP.NET
    HTML+CSS+JS
    HTML+CSS
    ASP.NET、WinForm、C#
    MSSQL
    WinFrom
    线性代数应该这样学一
  • 原文地址:https://www.cnblogs.com/OvOq/p/14725726.html
Copyright © 2020-2023  润新知