ZOJ 3656Bit Magic解题报告——2sat问题建图总结

2012年长春赛区B题,根据位运算的关系可以得到当某一个数的某一位取一个值时,跟其有相应运算关系的另一些数,就必须或者就不能取某些值,这样就可以利用2-sat问题解决,关于2-sat问题的加边技巧:

/*2-SAT问题,通俗的说就是有n对点(2n个点),从每对点中选出一个点,共选出n个点,而且要满足若干个这样的条件:某两点不能同时被选出。

    设一对点为x、~x,如果a被选出则b一定要被选出,就在图中加有向弧(a,b)表示这种关系。那么如果a,b(a!=b,a!=~b)不能同时被选出,那么加两条有向弧(a,~b),(b,~a)。这样由图的对称性可以证明,2-SAT有解等价于任取x、~x,x、~x不在一个强联通分量中。

    现在讨论一下特殊情况的意义。建图的过程中,如果a=b时,只用加一条有向弧(a,~a);这样也是有意义的,可表示a不能出现(~a必须被选出)。如果a=~b时,会加两条有向弧(a,a),(b,b),属于重边,无意义。这两种特殊情况都满足图的对称性:(a,~b)和(b,~a)同时存在,故不影响2-SAT的正确性。这样可以泛化一下满足条件,变成拓展的2-SAT:如果条件对应的若干个有向弧保证(a,~b)和(b,~a)同时存在,那么就可以转化最原始的2-SAT问题来解决。举个例子,如果有满足条件要求a必须出现,那么只需添加其对应的有向弧(~a,a)。这样2-SAT的应用范围就被大大增强了。(转载自http://blog.csdn.net/lethic/article/details/7803841)*/

我对2-sat加边的理解,比如说这道题中,如果a^b=0(a,b都表示一个二进制位),那意味着a,b是相同的那么我们加边就是在a=1和b=1之间b=1和a=1之间和a=0和b=0之间以及b=0和a=0之间加一条边,对于这种情况我们加边的原则是如果a……那么b……或者是如果b……那么a……这样保证的是选一个就必须选另一个(这种也是包括了转载文章中所说的选一个就不能选另一个的,可以推理出来必须选的关系,比如a,b不能同时出现,也就意味着选a必须选~b选b必须选~a),再看另一种情况a&nb=1的时候,很多人的第一反应都是在a=1和b=1之间加一条边,再在b=1和a=1之间加一条边,我们说这么加边有一个前提就是一个是另一个成立的条件,比如说如果a出现那么b必须出现意味着b是a出现的条件,但是当a&b=1的时候,这个还成立吗?a是b的条件?还是b是a的条件?很显然都不是,也就是说,这个时候已经不符合加边的条件了,他们没有谁是依赖谁成立的关系,这两种情况都必须成立,那么这个时候怎么办呢?我们加这么一条边~a->a和~b->b,我们不妨看下面一个例子

比如说a,b,a只能取0那么a->~a建一条边如果a为0的时候b必须要取要建一条边~a->b如果你又得到一个取b就必须取a那就意味着b->a又一条边,这样一来a和~a就在一个强连通分量里了 矛盾就产生了,我们用2-sat解决矛盾的原因就是因为当矛盾产生和两个冲突的值在同一个强连通分量中,必须要保证只要有矛盾就必须在同一个强连通分量中,同时保证在同一个强连通分量如果出现冲突的值意味着是矛盾的,这两个条件要同时成立,才能用强连通分量判断是否有矛盾,如果不加a->~a这条边,我们只能保证第二个条件成立,第一个条件在我说的例子中就不成立了,所以在这种情况下,a->~a是必须加的。
View Code
  1 #include<iostream>
  2 #include<cstdio>
  3 #include<cstring>
  4 #define N 1005
  5 using namespace std;
  6 int b[505][505];
  7 int head[N],t;
  8 struct node
  9 {
 10     int v,next;
 11 };
 12 node e[N*N*4];
 13 int stk1[N],stk2[N];
 14 int dfn[N],sc[N];
 15 int cnt0,cnt1,top1,top2;
 16 void init()
 17 {
 18     t=0;
 19     cnt0=cnt1=0;
 20     top1=top2=0;
 21     memset(head,-1,sizeof(head));
 22     memset(dfn,-1,sizeof(dfn));
 23     memset(sc,-1,sizeof(sc));
 24 }
 25 void add(int u,int v)
 26 {
 27     e[t].v=v;
 28     e[t].next=head[u];
 29     head[u]=t++;
 30 }
 31 void gabow(int u,int f)
 32 {
 33     dfn[u]=cnt0++;
 34     stk1[top1++]=u;
 35     stk2[top2++]=u;
 36     int i,j,v;
 37     for(i=head[u];i>=0;i=e[i].next)
 38     {
 39         v=e[i].v;
 40         if(v==f)
 41             continue;
 42         if(dfn[v]==-1)
 43             gabow(v,u);
 44         else if(sc[v]==-1)
 45             while(dfn[stk2[top2-1]]>dfn[v])
 46                     top2--;
 47     }
 48     if(stk2[top2-1]!=u)
 49         return;
 50     top2--;
 51     do
 52     {
 53         sc[stk1[--top1]]=cnt1;
 54     }while(stk1[top1]!=u);
 55     cnt1++;
 56 }
 57 int n;
 58 void build(int p)
 59 {
 60     int i,j;
 61     for(i=0;i<n;i++)
 62         for(j=0;j<n;j++)
 63         {
 64             if(i==j)
 65                 continue;
 66             if(i%2==1&&j%2==1)
 67             {
 68                 if(b[i][j]&(1<<p))
 69                 {
 70                     add(i+n,j);//i为0时j必须为1
 71                     add(j+n,i);//j为0时i必须为1
 72                 }
 73                 else
 74                 {
 75                     add(i,i+n);//i只能取0
 76                     add(j,j+n);//j只能取0
 77                 }
 78             }
 79             else if(i%2==0&&j%2==0)
 80             {
 81                 if(b[i][j]&(1<<p))
 82                 {
 83                     add(i+n,i);//i只能取1
 84                     add(j+n,j);//j只能取1
 85                 }
 86                 else
 87                 {
 88                     add(i,j+n);//i为1时j必为0
 89                     add(j,i+n);//j为1时i必为0
 90                 }
 91             }
 92             else
 93             {
 94                 if(b[i][j]&(1<<p))
 95                 {
 96                     add(i,j+n);
 97                     add(j,i+n);
 98                     add(i+n,j);
 99                     add(j+n,i);
100                 }
101                 else
102                 {
103                     add(i,j);
104                     add(j,i);
105                     add(i+n,j+n);
106                     add(j+n,i+n);
107                 }
108             }
109         }
110 }
111 bool solve()
112 {
113     int i,j,v;
114     for(i=0;i<n;i++)
115         if(b[i][i])
116             return false;
117     for(i=0;i<32;i++)
118     {
119         init();
120         build(i);
121         for(j=0;j<2*n;j++)
122             if(dfn[j]==-1)
123                 gabow(j,-1);
124         for(j=0;j<n;j++)
125             if(sc[j]==sc[j+n])
126                 return false;
127     }
128     return true;
129 }
130 int main()
131 {
132     int i,j,k;
133     while(scanf("%d",&n)!=EOF)
134     {
135         for(i=0;i<n;i++)
136             for(j=0;j<n;j++)
137                 scanf("%d",&b[i][j]);
138         if(solve())
139             printf("YES\n");
140         else
141             printf("NO\n");
142     }
143     return 0;
144 }
原文地址:https://www.cnblogs.com/caozhenhai/p/2725751.html