您的位置:首页 > 其它

POJ_3678 Katu Puzzle 2-sat

2012-05-04 15:09 344 查看
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 ;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: