http://poj.org/problem?id=3678
【题意】:有n个变量,每个可以取0或者1,再给出m组关系,每组关系都是两个变量进行运算可以得到的结果,运算有AND OR XOR三种,问能否根据这些关系,判断每个变量的取值。
【题解】:2-Sat问题,关键是要把所有情况考虑完全。用x表示该变量取0,x’表示取1,下面说下如何构图:
a and b == 1, 这种情况a和b必须取1,所以连边a->a', b->b'.(保证合法时a b一定取1)
a and b == 0, 这种情况a和b不能同时为1,所以连边a'->b, b'->a.
a or b == 1, 这种情况a和b不能同时为0,所以连边a->b', b->a'.
a or b == 0, 这种情况a和b必须同时为0,所以连边a'->a, b'->b.
a xor b == 1, 这种情况a和b取值要相反,所以连边a->b', a'->b, b->a', b'->a.
a xor b == 0, 这种情况a和b取值要相同,所以连边a->b, b->a, a'->b', b'->a'.
构图后强连通缩点判断有无解即可。
#define N 2111 vector<int> v[N]; stack<int> s; bool vis[N]; bool inStack[N]; int low[N],dfn[N]; int belong[N];//属于哪个强连通分量 int n,m,step,t; struct node{ int s,t; }p[N]; void init(){ int i; for(i=0;i<=2*n;i++){ v[i].clear(); vis[i] = 0; inStack[i] = 0; } while(!s.empty())s.pop(); } void add(int a,int b){ v[a].push_back(b); } //tarjan缩点 void tarjan(int u){ vis[u]=true; step++; s.push(u); inStack[u]=true; low[u]=step,dfn[u]=step; int i,j; for(i=0;i<v[u].size();i++) { int x=v[u][i]; if(!vis[x]) { tarjan(x); low[u]=min(low[u],low[x]); } else if(inStack[x]) low[u]=min(low[u],dfn[x]); } if(low[u]==dfn[u]) { t++; while(1) { int x=s.top(); s.pop(); belong[x]=t; inStack[x]=false; if(x==u)break; } } } void solve(){ t = 0,step = 0; int i,j; for(i=0;i<2*n;i++){ if(!vis[i])tarjan(i); } for(i=0;i<n;i++){ if(belong[i] == belong[i+n]){ puts("NO"); return ; } } puts("YES"); } int main(){ while(scanf("%d%d",&n,&m) != -1){ init(); char str[5]; while(m--){ int a,b,c; scanf("%d%d%d%d",&a,&b,&c);scanf("%s",str); if(strcmp(str,"AND") == 0){ if(c == 1){ add(a,a+n); add(b,b+n); } else { add(b+n,a); add(a+n,b); } } else if(strcmp(str,"OR") == 0){ if(c == 1){ add(a,b+n); add(b,a+n); } else { add(a+n,a); add(b+n,b); } } else if(strcmp(str,"XOR") == 0){ if(c == 1){ add(a,b+n); add(b+n,a); add(a+n,b); add(b,a+n); } else { add(a,b); add(b,a); add(a+n,b+n); add(b+n,a+n); } } } solve(); } return 0; }