PKU 3678 Katu Puzzle - 2-SAT判定

题目大意

n个布尔变量Xi,现在有m个约束条件,给出Xi op Xj = c,给出每个i j c 和 op。其中op可能是AND,OR,XOR。要求这n个布尔变量是否存在一组可能的取值,使得满足所有的约束条件。

分析:

题目可以转换为一个2-SAT问题。首先将每个布尔变量拆成取值为0和取值为1两个节点。给出的约束条件可以这样确定:Xi op Xj = c,那么Xi&Xj取值不等于c的那些取值就会产生冲突。例如X1 & X2 = 1,那么(X1=0,X2=0),(X1=0,X2=1)和(X1=1,X2=0)这三组取值便产生冲突。

 

  1. /*
  2. PKU3678 Katu Puzzle
  3. */
  4. #include <stdio.h>
  5. #include <memory.h>
  6. #define clr(a) memset(a,0,sizeof(a))
  7. #define N 2005
  8. #define M 2000005
  9. #define _DB if(1)
  10. /*************************************************/
  11. typedef struct StrNode{
  12.     int j; struct StrNode* next;
  13. }Node;
  14. int memp; Node mem[M];
  15. void addEdge(Node* e[],int i,int j){
  16.     Node* p = &mem[memp++]; p->j=j; p->next=e[i]; e[i]=p;
  17. }
  18. int g_DFS_First;
  19. void DFS_conn(Node* e[],int i,int mark[],int f[],int* nf){
  20.     int j; Node* p;
  21.     if(mark[i]) returnelse mark[i]=1;
  22.     if(!g_DFS_First) f[i]=*nf;  //反向搜索,获取连通分量编号
  23.     for(p=e[i];p!=NULL;p=p->next) DFS_conn(e,p->j,mark,f,nf);
  24.     if(g_DFS_First) f[(*nf)++]=i;   //正向搜索,获取时间戳
  25. }
  26. int Connection (Node* e[],int n,int con[]){
  27.     int i,j,k,mark[N],ncon,time[N],ntime; Node *p,*re[N];
  28.     for(clr(re),i=0;i<n;i++)for(p=e[i];p!=NULL;p=p->next) addEdge(re,p->j,i);
  29.     
  30.     g_DFS_First=1; clr(mark); clr(time);
  31.     for(ntime=i=0;i<n;i++) if(!mark[i]) DFS_conn(e,i,mark,time,&ntime);
  32.     g_DFS_First=0; clr(mark); clr(con);
  33.     for(ncon=0,i=n-1;i>=0;i--) if(!mark[time[i]])
  34.     { DFS_conn(re,time[i],mark,con,&ncon); ncon++; }
  35.     return ncon;
  36. }
  37. /*
  38. 2-SAT问题 
  39. 参数:gp[]数组传入n组节点的两个编号[0,2n-1]
  40.     cf[]数组传入m对相互冲突的节点编号。
  41.     返回问题是1否0有解。
  42. */
  43. int Two_SAT(int gp[][2],int n,int cf[][2],int m){
  44.     int i,j,x,y,k,n2=n+n;
  45.     Node *e[N],*es[N],*p;
  46.     int opp[N],con[N],ncon; //强连通分量数组
  47.     //get opp
  48.     for(k=i=0;i<n;i++){ opp[gp[i][0]]=gp[i][1];opp[gp[i][1]]=gp[i][0]; }
  49.     //create map e[]
  50.     clr(e); memp=0;
  51.     for(k=0;k<m;k++){
  52.         i=cf[k][0]; j=cf[k][1];
  53.         addEdge(e,i,opp[j]); addEdge(e,j,opp[i]);
  54.     }
  55.     //Shrink Connections
  56.     ncon = Connection(e,n2,con);
  57.     //judge
  58.     for(i=0;i<n2;i++) if(con[i]==con[opp[i]]) return 0;
  59.     return 1;
  60. }
  61. /****************************************/
  62. int ID(int i,int k){return i*2+k;}
  63. int c[3][2][2]={{{0,0},{0,1}},
  64.                 {{0,1},{1,1}},
  65.                 {{0,1},{1,0}}};
  66.                 
  67. int gp[N][2],cf[M][2],ret[N];               
  68. int main()
  69. {
  70.     int i,j,k;
  71.     int n,m,cn,p,q,v,r;
  72.     char s[10];
  73.     
  74.     while(scanf("%d%d",&n,&m)!=EOF){
  75.         for(i=0;i<n;i++) for(k=0;k<2;k++)
  76.             gp[i][k]=ID(i,k);
  77.         //input
  78.         cn=0;
  79.         for(k=0;k<m;k++){
  80.             scanf("%d%d%d%s",&i,&j,&v,s);
  81.             if(s[0]=='A')r=0; if(s[0]=='O')r=1; if(s[0]=='X')r=2;
  82.             for(p=0;p<2;p++) for(q=0;q<2;q++) if(c[r][p][q]!=v){
  83.                 cf[cn][0]=ID(i,p);
  84.                 cf[cn++][1]=ID(j,q);
  85.             }
  86.         }
  87.         //judge
  88.         if(Two_SAT(gp,n,cf,cn)) puts("YES");
  89.         else puts("NO");
  90.     }
  91.     
  92.     return 0;
  93. }
.
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值