题目链接:https://cn.vjudge.net/contest/209474#problem/C
题目大意:
一共有m个提案,n个政客,每个政客都会对一些提案(最多四个)提出自己的意见——通过或者不通过。一个政客获得满意结果,当且仅当他的意见被满足了超过(注意是超过,即大于)一半,请问有没有这样的方案,使n个政客都满意?如果存在,输出所有方案。(这个所有的意思是一个提案如果只能是“通过”决策(在所有方案里都是通过),输出’y‘,只能不通过输出’n’,可以通过也可以不通过(有的方案是y,有的是n)输出‘?’)
解题思路:
本来想写在《浅谈2-SAT》里,想了想由于这道题比较巧妙,还是单独提出来写。巧妙之处在于1、寻找所有可行方案。2、问题到标准模型的转化。
先谈一下第二点。
一个政客满意,当且仅当他有一半以上的观点被采纳。也就是说,对于只提出一个提案和两个提案的人,只有他提出的所有观点都被采纳,这个人才会满意。对于提出三个提案的人,至少要满足两个观点(最多有一个不满足);对于提出四个提案的人,至少要满足三个观点(最多有1个不满足)。
每个提案有通过和不通过两种互斥状态,对于观点数量小于等于2的人,他所提出的提案有固定状态(不对称图),对于观点数大于等于3的人,我们如果令其中一个观点为不满足,那么其他的观点都必须满足。
由此我们建立了一个部分确定值的2-SAT图像。
再谈第一点。
这道题的难点其实我自我感觉在第一点。令人惊讶的是,第一点并没有什么高端的处理方法,仅仅是暴力枚举,如果一个提案两种状态的2-SAT图像都成立,那么就是?,只成立一种就是对应结果。如果两种都不成立那么就无解。
下面放有详细注释的9msAC代码并附了几组数据debug(欢迎hack):
1 /* by Lstg */ 2 /* 2018-03-07 00:33:58 */ 3 4 #include<stdio.h> 5 #include<iostream> 6 #include<vector> 7 #include<string.h> 8 #define MAXN 5100 9 using namespace std; 10 11 vector<int>g[MAXN]; 12 13 bool mark[MAXN],f[MAXN]; 14 int stk[MAXN],top,n,lq[MAXN]; 15 char ans[500]; 16 17 int _max(int a,int b){return a>b?a:b;} 18 int _abs(int x){return x<0?-x:x;} 19 20 void _clean(){ 21 22 for(int i=0;i<=2*n+1;i++){ 23 g[i].clear(); 24 mark[i]=0; 25 f[i]=false; 26 } 27 } 28 29 bool _dfs(int x){ 30 31 if(mark[x^1])return false; 32 if(mark[x])return true; 33 mark[x]=true; 34 stk[++top]=x; 35 for(int i=0;i<g[x].size();i++) 36 if(!_dfs(g[x][i]))return false; 37 return true; 38 } 39 40 bool _Twosat(){ 41 42 for(int i=2;i<=2*n+1;i+=2) 43 if(!mark[i]&& !mark[i+1]){ 44 top=0; 45 if(!_dfs(i)){ 46 while(top) 47 mark[stk[top--]]=false; 48 if(!_dfs(i+1))return false; 49 } 50 } 51 return true; 52 } 53 54 void _addside(int k){ 55 56 for(int i=1;i<=k;i++) 57 for(int j=1;j<=k;j++) 58 if(j!=i) 59 g[lq[i]^1].push_back(lq[j]);//lq[i]^1表示状态的反面,即不满足。在此情况下其他的状态必须满足 60 61 } 62 63 bool _check(){ 64 65 int i; 66 for(i=2;i<=2*n+1;i++) 67 if(f[i]&&!_dfs(i))return false;//如果不能满足固定的状态值就无解 68 memcpy(f,mark,sizeof(mark));//f数组已经没用了,用来当过渡 69 //printf("n=%d ",n); 70 if(!_Twosat())return false;//如果满足了所有固定状态却不能满足整个2-SAT图,无解 71 //puts("haha! "); 72 for(i=1;i<=n;i++){ 73 memcpy(mark,f,sizeof(f));//固定状态都满足的残余2-SAT图作为起始图像 74 top=0; 75 if(!(_dfs(2*i)&&_Twosat()))ans[i]='n';/*这里可能有点难理解,我写的确实很绕,没必要跟我一样, 76 这里的意思是如果不满足该提案通过,那么这个提案就一定是不通过(因为无解的情况已经全排除了,一定有解,这个提案通过却不满足图像,那么这个提案一定通不过)*/ 77 else{//如果提案可以通过 78 memcpy(mark,f,sizeof(f)); 79 top=0; 80 if(_dfs(2*i+1)&&_Twosat())ans[i]='?';//验证是否也可以不通过 81 else ans[i]='y'; 82 } 83 84 } 85 return true;//返回有解 86 } 87 88 89 90 int main(){ 91 92 //freopen("C.txt","w",stdout); 93 int m,k,i,j,a,x; 94 char ch[8]; 95 int T=0; 96 while(true){ 97 98 scanf("%d%d",&n,&m); 99 if(m+n==0)return 0; 100 _clean(); 101 102 for(i=1;i<=m;i++){ 103 scanf("%d",&k); 104 105 for(j=1;j<=k;j++){ 106 scanf("%d%s",&x,ch); 107 108 a=(ch[0]=='y'?0:1);//2*ii表示通过,2*ii+1表示不通过 109 110 if(k<=2)f[x*2+a]=true;//f数组表示有确定状态的提案 111 else lq[j]=x*2+a;//lq数组为一会儿构图的媒介 112 } 113 if(k>=3)_addside(k);//用lq数组构图 114 } 115 printf("Case %d: ",++T); 116 if(!_check())printf("impossible "); 117 else{ 118 for(i=1;i<=n;i++) 119 putchar(ans[i]); 120 putchar(10); 121 } 122 } 123 return 0; 124 } 125 /* 126 1 1 127 1 1 n 128 1 1 129 1 1 y 130 131 3 2 132 3 1 y 2 y 3 n 133 3 1 n 2 n 3 y 134 135 3 2 136 3 1 y 2 n 3 n 137 3 1 n 2 n 3 n 138 139 3 2 140 3 1 y 2 y 3 n 141 3 1 y 2 n 3 n 142 143 4 2 144 4 1 y 2 y 3 n 4 n 145 3 2 n 3 n 4 n 146 3 2 147 3 1 y 2 y 3 n 148 3 1 y 2 n 3 n 149 150 */