题解 P4782 【【模板】2-SAT 问题】
kuansoudafahao · · 题解
2-SAT 问题
我们发现,题目的要求是在两个条件中任选一个满足。也就是说,如果我们不选A条件,就必须要选B条件;同理,我们不选B条件,就必须要选A条件。
现在,我们假设边(A,B)表示选了A条件,就必须要选B条件。那么根据上面的推论,我们发现现在我们应该连边(非A条件,B条件),(非B条件,A条件)。
这个时候我们能发现,如果A条件和非A条件在同一个强连通分量里面,证明选了A条件之后我们就要选非A条件,这自相矛盾。这个时候无解。
顺便给大家推荐几道相关的题目:
JSOI2010满汉全席
POJ2723
#include <cstdio>
#define min(a,b) (a<b?a:b)
const int maxn=1e6+10;
struct EDGE
{
int t,next;
}edge[maxn<<2];
int head[maxn<<1],low[maxn<<1],dfn[maxn<<1],stack[maxn<<1],scc[maxn<<1];
bool in[maxn<<1];
int cur,n,tim,top,cnt,m;
void add(int u,int v)
{
edge[++cur].t=v;
edge[cur].next=head[u];
head[u]=cur;
}
void tarjan(int u)
{
low[u]=dfn[u]=++tim;
stack[top++]=u;in[u]=true;
for(int i=head[u]; i; i=edge[i].next)
{
int v=edge[i].t;
if(!dfn[v])
{
tarjan(v);
low[u]=min(low[u],low[v]);
}else if(in[v])
low[u]=min(low[u],dfn[v]);
}
if(low[u]==dfn[u])
{
int v;
cnt++;
do
{
v=stack[--top];
in[v]=false;
scc[v]=cnt;
}while(u!=v);
}
}
bool two_SAT()
{
for(int i=1; i<=2*n; i++)
if(!dfn[i])
tarjan(i);//tarjan找强连通分量
for(int i=1; i<=n; i++)
if(scc[i]==scc[i+n])//a条件和非a条件在同一个强连通分量,原问题无解
return false;
return true;
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1; i<=m; i++)
{
int a,b,aval,bval;
scanf("%d%d%d%d",&a,&aval,&b,&bval);
int nota=aval^1,notb=bval^1;//这里我用a表示条件a选0的情况,a+n表示条件a选1的情况。
add(a+nota*n,b+bval*n);//连边(非a,b)
add(b+notb*n,a+aval*n);//连边(非b,a)
}
if(two_SAT())
{
printf("POSSIBLE\n");
for(int i=1; i<=n; i++)
printf("%d ",scc[i]>scc[i+n]);
}else
printf("IMPOSSIBLE");
return 0;
}