http://poj.org/problem?id=3678
题意:给你一个有N个点的图,和其中的某一些边,每条边都可以记为:(a,b,c,d),其中a,b代表的是边所依附的两个结点,d表示的是一种运算符,0: AND , 1 :OR , 2:XOR,c表示a,b顶点的值运用d运算符之后的结果应为c。需要给每个结点都赋一个值,使得满足所有的约束条件。
思路:2-sat问题,首先我们可以看出,所有的运算都是bool运算,因此每个结点的值要不是0,就是1,只有两种选择,这就是典型的2-sat问题,而且只要判定是否存在这样的结点值的分配就可以了,属于2-sat中的判定问题。然后我们就需要考虑建图,对于每个结点i,如果它取值为0 ,则表示选择了2*i-1号结点,如果取值为1,则表示选择了2*i号结点。这样我们就可以根据每个结点的出边来建图了,具体规则如下:
a && b = 1 : 2*a -> 2*b (两个都选择1) , 2*a-1->2*a(a号结点选0的话,无论b选什么都不会成立,所有这里就添加一条矛盾的边,表示如果结点a选择0,则结点1也就要选择1,这里在最后的图中2*a和2*a-1就会在同一个强连通分量当中,具体的证明可以用2-sat中边的对称性来证明。)
a && b = 0 : 2*a -> 2*b-1
a || b = 1 : 2*a-1 -> 2*b ;
a || b = 0 : 2*a-1 -> 2*b-1 ; 2*a -> 2*a-1 ;
a ^ b = 1 : 2*a ->2*b-1 ; 2*a-1 -> 2*b
a ^ b = 0 : 2*a -> 2*b ; 2*a - 1 -> 2 *b -1
上述的所有边都没有考虑对称的那条,这是因为原本建的图就是双向的,对称的那条边在下次搜索的时候自然会搜索到。
代码:
#include<stdio.h> #include<string.h> int N ,M ; const int MAXE = 1000010 ; struct Node{ int num , next ,flag ,val; }edge[MAXE*2] ; int root[1010] , ec ; struct Node1{ int num, next ; }ex[1000*1000] ; int r[1010] , ee ; int rr[1010*2] ; void init(){ memset(root, -1, sizeof(root)); ec = 0 ; memset(r, -1, sizeof(r)); ee = 0 ; } void add(int a,int b , int c, int d){ edge[ec].num = b ; edge[ec].next = root[a] ; edge[ec].flag = d ; edge[ec].val = c ; root[a] = ec++ ; } void add2(int a,int b){ ex[ee].num = b ; ex[ee].next = r[a] ; r[a] = ee++ ; } const int MAXN = 1010*2 ; int dfn[MAXN] , low[MAXN] , stack[MAXN] ,col[MAXN]; bool in[MAXN] ; int top,idx, Bcnt ; void tarjin(int u){ int v ; low[u] = dfn[u] = ++idx ; stack[++top] = u ; in[u] =1 ; for(int i=r[u] ;i!=-1;i=ex[i].next){ v = ex[i].num ; if( !dfn[v] ){ tarjin(v) ; if( low[v] < low[u]) low[u] = low[v] ; } else if( in[v] && dfn[v] < low[u]) low[u] = dfn[v] ; } if( low[u] == dfn[u] ){ Bcnt++; do{ v = stack[top--] ; in[v] = 0 ; col[v] = Bcnt ; }while(u!=v) ; } } void solve(){ idx = Bcnt = top= 0 ; memset(dfn , 0, sizeof(dfn)); memset(in, 0 ,sizeof(in)); for(int i=1;i<=2*N;i++){ if( !dfn[i] ) tarjin(i) ; } bool ok = 1 ; for(int i=1;i<=N;i++){ if( col[2*i-1]==col[2*i] ){ ok = 0 ; break ; } } if( ok ) printf("YES\n"); else printf("NO\n"); } int main(){ int a, b ,c ,d; char ch[10] ; while(scanf("%d%d",&N,&M) == 2){ init() ; for(int i=1;i<=M;i++){ scanf("%d%d%d%s",&a,&b,&c,ch) ; a ++ ; b ++ ; if(ch[0]=='A') d = 0 ; else if(ch[0]=='O') d = 1 ; else d = 2 ; add(a,b,c,d); add(b,a,c,d); } for(int u=1;u<=N;u++){ for(int j=root[u] ;j!=-1;j=edge[j].next){ int v = edge[j].num ; int val = edge[j].val ; int f = edge[j].flag ; if(val == 0){ if(f == 0){ //AND add2(2*u,2*v-1); } else if(f == 1){ //OR add2(2*u-1, 2*v-1); add2(2*u,2*u-1); } else{ //XOR add2(2*u,2*v); add2(2*u-1,2*v-1); } } else{ if(f == 0){ add2(2*u, 2*v); add2(2*u-1,2*u); } else if(f == 1){ add2(2*u-1,2*v); } else{ add2(2*u,2*v-1); add2(2*u-1,2*v); } } } } solve() ; } return 0 ; }