[dfs][2-sat] JZOJ P3796 议案决定
题解
-
就是一道2-SAT问题
-
点u->v代表u发生则v必然发生
-
每一个条件可以拆为这样的两个条件
-
如1 Y 2 N 对应的是 1N->2N 2Y->1Y
-
我们把1Y 1N称为对立节点
-
那么我们怎么找到将它们连起来且没有对立节点的路径(就是可以找到一条路径,因为对立节点没有边相连)
-
可以用dfs解决。
-
如果对某个事务赞成就必须对该事务反对(记为事件A),且对某个事务反对就必须对该事务赞成(记为事件B)
-
则不可能满足所有条件
-
否则: 若事件A成立,则对该事务必须反对
-
若事件B成立,则对该事务必须赞成
代码
1 #include<cstdio> 2 #include<iostream> 3 #include<algorithm> 4 #include<memory.h> 5 using namespace std; 6 int next[16011],last[16011],head[16011]; 7 int f[16011],k[16022][3],n,m,x,z,num,tot; 8 bool flag,must[16011][3]; 9 void insert(int x,int y) 10 { 11 next[++tot]=head[x]; 12 head[x]=tot; 13 last[tot]=y; 14 } 15 void dfs(int x) 16 { 17 if (flag==false) return; 18 f[x]=num; 19 if (f[x]==num&&f[x^1]==num) flag=false; 20 if (flag==false) return; 21 int i=head[x],j; 22 while (i!=0) 23 { 24 j=last[i]; 25 if (f[j]!=num) dfs(j); 26 i=next[i]; 27 } 28 29 } 30 int main() 31 { 32 scanf("%d%d",&n,&m); 33 char ch; 34 memset(must,true,sizeof(must)); 35 for (int i=1;i<=m;i++) 36 { 37 scanf("%d",&x); 38 while(ch=getchar(),ch!='Y'&&ch!='N'); 39 if (ch=='Y') z=1; else z=0; 40 if (z==1) k[i][1]=x*2+1; else k[i][1]=x*2; 41 scanf("%d",&x); 42 while(ch=getchar(),ch!='Y'&&ch!='N'); 43 if (ch=='Y') z=1; else z=0; 44 if (z==1) k[i][2]=x*2+1; else k[i][2]=x*2; 45 insert(k[i][2]^1,k[i][1]); 46 insert(k[i][1]^1,k[i][2]); 47 } 48 for (int i=1;i<=n;i++) 49 { 50 flag=true; 51 num++; 52 f[2*i]=num; 53 dfs(2*i); 54 if (flag==false) must[i][2]=false; 55 flag=true; 56 num++; 57 f[2*i+1]=num; 58 dfs(2*i+1); 59 if (flag==false) must[i][1]=false; 60 } 61 flag=true; 62 for (int i=1;i<=n;i++) if (must[i][1]==false&&must[i][2]==false) flag=false; 63 if (flag==false) printf("IMPOSSIBLE\n"); 64 else 65 for (int i=1;i<=n;i++) 66 { 67 if (must[i][2]==true&&must[i][1]==true) printf("?"); 68 if (must[i][2]==false&&must[i][1]==true) printf("Y"); 69 if (must[i][2]==true&&must[i][1]==false) printf("N"); 70 } 71 return 0; 72 }