POJ_3678 Katu Puzzle 2-sat

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 ;
}


你可能感兴趣的:(POJ_3678 Katu Puzzle 2-sat)