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;
}










posted @ 2021-05-02 14:58  OvO1  阅读(78)  评论(0编辑  收藏  举报