题意:
给你四个数,n个人(每个人不是好人就是坏人),m个二元组,再给你x个整数,y个整数(n<=1e3,m<=1e5,x+y<=n)
m个二元组(a,b)表示 a,b不能同时为好人或坏人。
x个整数,每个整数c表示c一定是好人。
y个整数,每个整数d表示d一定是坏人。
求是否存在一组合法的解使得满足上面所有条件。有的话输出YES,否则输出NO。(注意大小写)
思路:典型的二分图判定。对于x和y直接染成不同的颜色,有矛盾的话直接标记为NO。
然后套个二分图判定模板就行了。用2-sat也可以做。
二分图判定做法代码:
#include<bits/stdc++.h>
#define ll long long
#define inf 0x3f3f3f3f3f3f3f3fLL
using namespace std;
const int maxn=100010;
int fcount(int x) //求数x二进制下所含1的个数
{
int s=0;
while(x){
s++;
x&=(x-1);
}
return s;
}
vector<int> v[100005], a, b;
int ok = 1, lor[100005],lorr[100005],vis[100005];
int C[maxn],tot;
void Sech(int x);
int main(void)
{
int i, n, m, c, d,nn,mm;
while(scanf("%d%d%d%d",&n,&m,&nn,&mm)!=EOF){
memset(lor,0,sizeof(lor));
memset(lorr,0,sizeof(lorr));
for(i=0;i<=n;i++)
v[i].clear(),vis[i]=0;
ok=1;tot=0;a.clear();b.clear();
for(i=1;i<=m;i++)
{
scanf("%d%d", &c, &d);
if(!vis[c]){vis[c]=1;C[++tot]=c;}
if(!vis[d]){vis[d]=1;C[++tot]=d;}
v[c].push_back(d);
v[d].push_back(c);
}
int x;
for(int i=0;i<nn;i++)
{
scanf("%d",&x);
vis[x]=1;
lor[x]=1;
lorr[x]=-1;
}
for(int i=0;i<mm;i++)
{
scanf("%d",&x);
if(lor[x]==1) {ok=0;}
vis[x]=1;
lor[x]=-1;
lorr[x]=1;
}
for(int i=1;i<=n;i++)
if(!vis[i]) {ok=0;break;}
for(int i=1;i<=n;i++)
if(lor[i])for(int j=0;j<v[i].size();j++)
{
if(lor[v[i][j]]==lor[i]) {ok=0;break;}
}
if(!ok) {puts("NO");continue;}
for(int ii=1;ii<=tot;ii++)
{
i=C[ii];
if(lor[i]==0)
{
lor[i] = 1;
a.push_back(i);
Sech(i);
}
}
if(ok==0)
{
// cout<<" *"<<endl;
ok=1;a.clear();b.clear();
for(int i=0;i<=n;i++)
lor[i]=lorr[i];
for(int ii=1;ii<=tot;ii++)
{
int i=C[ii];
if(lor[i]==0)
{
lor[i] = 1;
a.push_back(i);
Sech(i);
}
}
if(ok==0) puts("NO");
else puts("YES");
}
else printf("YES\n");
}
return 0;
}
void Sech(int x)
{
int i, t;
if(ok==0)
return;
for(i=0;i<v[x].size();i++)
{
t = v[x][i];
if(lor[t]==lor[x])
ok = 0;
else
{
if(lor[x]==1 && lor[t]==0)
{
b.push_back(t), lor[t] = -1;
Sech(t);
}
else if(lor[x]==-1 && lor[t]==0)
{
a.push_back(t), lor[t] = 1;
Sech(t);
}
}
}
}
2-sat代码(by 队友 https://blog.csdn.net/lml11111/article/details/82932185):
#include<bits/stdc++.h>
using namespace std;
const int maxn=10000+10;
bool vis[maxn*2];
struct TwoSAT
{
int n;//原始图的节点数(未翻倍)
vector<int> G[maxn*2];//G[i]==j表示如果mark[i]=true,那么mark[j]也要=true
bool mark[maxn*2];//标记
int S[maxn*2],c;//S和c用来记录一次dfs遍历的所有节点编号
void init(int n)
{
this->n=n;
for(int i=0;i<2*n;i++) G[i].clear();
memset(mark,0,sizeof(mark));
}
//加入(x,xval)或(y,yval)条件
//xval=0表示假,yval=1表示真
void add_clause(int x,int xval,int y,int yval)
{
x=x*2+xval;
y=y*2+yval;
G[x^1].push_back(y);
G[y^1].push_back(x);
}
//从x执行dfs遍历,途径的所有点都标记
//如果不能标记,那么返回false
bool dfs(int x)
{
if(mark[x^1]) return false;//这两句的位置不能调换
if(mark[x]) return true;
mark[x]=true;
S[c++]=x;
for(int i=0;i<G[x].size();i++)
if(!dfs(G[x][i])) return false;
return true;
}
//判断当前2-SAT问题是否有解
bool solve()
{
for(int i=0;i<2*n;i+=2)
if(!mark[i] && !mark[i+1])
{
//printf("fsd sfdg %d\n",i);
c=0;
if(!dfs(i))
{
while(c>0) mark[S[--c]]=false;
if(!dfs(i+1)) return false;
}
}
return true;
}
}fun;
int main()
{
int i,j,x,y,aa,bb,m,n;
while(~scanf("%d%d%d%d",&n,&m,&x,&y))
{
fun.init(n);
memset(vis,0,sizeof(vis));
for(i=0;i<m;i++)
{
scanf("%d%d",&aa,&bb);
aa--;
bb--;
fun.add_clause(aa,1,bb,1);
fun.add_clause(aa,0,bb,0);
vis[aa]=1;
vis[bb]=1;
}
for(i=0;i<x;i++)
{
scanf("%d",&aa);
aa--;
vis[aa]=1;
fun.add_clause(aa,1,aa,1);
}
for(i=0;i<y;i++)
{
scanf("%d",&aa);
aa--;
vis[aa]=1;
fun.add_clause(aa,0,aa,0);
}
bool flag;
flag=-1;
for(i=0;i<n;i++)
{
if(vis[i]==0)
{
flag=0;
break;
}
}
if(flag==0)
{
printf("NO\n");
continue;
}
flag=fun.solve();
if(flag)
printf("YES\n");
else
printf("NO\n");
}
}