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