昨天学弟问我G题是不是2-SAT,尴尬的是我不知道那是什么,所以今天特地来学一下,先来个简单的
先看一下最经典的题HDU1814和平委员会
题意:输入n,m代表n个国家,每个国家 i 有两个代表分别是 2*i-1和2*i.m代表有m对冤家,每对冤家不能一起出现在委员会中,问能不能让所有国家都出一个代表构成委员会,可以的话输出n个国家所选出的代表标号,否则输出NIE
就粘一下输入输出吧
Sample Input
3 2
1 3
2 4
Sample Output
1
4
5
这道题感觉跟2-SAT(仅限我现在理解的)有关系,还感觉没啥关系
oth(x)代表和x是一个国家的那个人的编号
做法就是如果 i 和 j 是冤家那么 选 i 就要选 oth(j) ,选 j 就要选 oth(i) 然后从1~2*n开始遍历
数组 f 初始是0,1代表选中,2代表不选
对于第 i 个遇到 f[i] 是0,那么先试试 选 i ,即 f[i]=1,f[oth(i)]=2
如果选了一个那么边相连的就都要选,如果边相连是2了,说明决策不对则f[i]=2,f[oth(i)]=1,若还不行那么无解
最后把f[i]=1的 i 输出就可以了
#include<stdio.h> #include<string.h> #define rep(i,j,k) for(int i=j;i<=k;++i) #define oth(x) (x%2==0?x-1:x+1) struct s { int to,next; } edge[40005]; int head[16005]; int cnt; void add(int x,int y) { edge[cnt].to=y; edge[cnt].next=head[x]; head[x]=cnt++; } int tot; int f[16005],ff[160005]; bool paint(int x) { if(f[x]!=0) return f[x]%2; f[x]=1; f[oth(x)]=2; ff[tot++]=x; for(int i=head[x]; i!=-1; i=edge[i].next) { if(!paint(edge[i].to)) return false; } return true; } int n; bool solve() { rep(i,1,2*n) { if(f[i]) continue; tot=0; if(!paint(i)) { rep(j,0,tot-1) { f[ff[j]]=0; f[oth(ff[j])]=0; } if(!paint(oth(i))) return false; } } return true; } int main() { int m; while(scanf("%d %d",&n,&m)!=EOF) { memset(head,-1,sizeof(head)); memset(f,0,sizeof(f)); cnt=0; int x,y; rep(i,1,m) { scanf("%d %d",&x,&y);//oth(x)是x的搭档 add(x,oth(y));//选x就只能选y的搭档 add(y,oth(x)); } if(solve()) { rep(i,1,2*n) if(f[i]==1) printf("%d ",i); } else printf("NIE "); } }