poj3678-Katu Puzzle
有\(n\)个bool变量和\(m\)条限制,每条限制为a op b=c的形式,其中op为逻辑运算,问是否存在合法解。
分析
这一类问题叫做2-SAT问题,有很多个两种取值的变量,其中有一些限制条件。2-SAT问题我们采用建模的方式,如果\(a\)选就一定要选\(b\),那么连有向边\((a,b)\)。2-SAT问题有解的条件就是\(a\)和\(!a\)不在同一个强连通分量中,也就是说不产生矛盾。
几个基本逻辑运算的连边如下,其他的都可以转化为这几种:
- a=true,\(!a\to a\)
- a=false,\(a\to !a\)
- a or b=true,\(!a\to b,!b\to a\)
- a or b=false,等价于\(a=false,b=false\)
- a and b=true,等价于\(a=true,b=true\)
- a and b=false,\(a\to !b,b\to !a\)
- a xor b=true,\(a\to !b,!a\to b,b\to !a,!b\to a\)
- a xor b=false,\(a\to b,!a\to !b,b\to a,!b\to !a\)
- a=b,等价于a xor b=false
- \(a\ne b\),等价于a xor b=true
这道题就这样做完啦。
代码
#include<cstdio>
#include<cctype>
#include<cstring>
#include<algorithm>
using namespace std;
int read() {
int x=0,f=1;
char c=getchar();
for (;!isdigit(c);c=getchar()) if (c=='-') f=-1;
for (;isdigit(c);c=getchar()) x=x*10+c-'0';
return x*f;
}
const int maxn=2e3+10;
const int maxm=4e6+10;
int dfn[maxn],low[maxn],dft=0,id[maxn],sta[maxn],top=0,col=0;
bool ins[maxn];
struct edge {
int v,nxt;
};
struct graph {
edge e[maxm];
int h[maxn],tot;
graph ():tot(0) {}
void add(int u,int v) {
e[++tot]=(edge){v,h[u]};
h[u]=tot;
}
void Tarjan(int x) {
dfn[x]=low[x]=++dft;
sta[++top]=x;
ins[x]=true;
for (int i=h[x],v=e[i].v;i;i=e[i].nxt,v=e[i].v) if (!dfn[v]) {
Tarjan(v);
low[x]=min(low[x],low[v]);
} else if (ins[v]) low[x]=min(low[x],dfn[v]);
if (low[x]==dfn[x]) {
++col;
do id[sta[top--]]=col; while (sta[top+1]!=x);
}
ins[x]=false;
}
bool run(int n) {
for (int i=1;i<=n;++i) if (id[i]==id[n+i]) return false;
return true;
}
} A;
int main() {
#ifndef ONLINE_JUDGE
freopen("test.in","r",stdin);
#endif
int n=read(),m=read();
for (int i=1;i<=m;++i) {
int x=read()+1,y=read()+1,z=read();
static char op[8];
scanf("%s",op);
if (op[0]=='A' && z) A.add(x+n,x),A.add(y+n,y); else
if (op[0]=='A' && !z) A.add(x,y+n),A.add(y,x+n); else
if (op[0]=='O' && z) A.add(x+n,y),A.add(y+n,x); else
if (op[0]=='O' && !z) A.add(x,x+n),A.add(y,y+n); else
if (op[0]=='X' && z) A.add(x,y+n),A.add(x+n,y),A.add(y,x+n),A.add(y+n,x); else
if (op[0]=='X' && !z) A.add(x,y),A.add(x+n,y+n),A.add(y,x),A.add(y+n,x+n);
}
for (int i=1;i<=(n<<1);++i) if (!id[i]) A.Tarjan(i);
puts(A.run(n)?"YES":"NO");
return 0;
}