poj3648 2-SAT
题目链接:http://poj.org/problem?id=3648
题意有点纠结:
一堆夫妇去参加一对新人的婚礼。人们坐在一个很长很长的桌子的两侧(面对面)。新郎新娘在桌子头面对面座。
新娘不希望看见她对面的一排有一对夫妇坐着(夫妇需要分开两排座)。
同时,一些人之间有通奸关系,新娘也不希望有通奸关系的人同时坐在她对面的一排。
问你可否满足新娘的要求,可以的话,输出一种方案
思路:
把丈夫和新娘同一边看做点i,在新娘对面看做i+n。然后根据通奸关系进行构图。因为新郎也有可能有通奸关系,
所以构图时加入新郎点0,增加一条边,0-->0+n。然后就是求解2-SAT问题了。
第一道2-SAT问题,好几天没刷题了,惭愧ing,好好加油!!前端时间写了一道强连通分量+拓扑排序,这道题目就简单多了:其实
也是强连通分量+拓扑排序,但是建图是还是关键的。
强烈推荐去看看《由对称性解2-SAT问题》,讲的很清晰。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <stdio.h> 2 #include <queue> 3 #include <string.h> 4 #include <iostream> 5 using namespace std; 6 #define M 2005 7 #define N 3000010 8 int head[M],head1[M];//head[],edgeNum,edge[]是建的图 9 int edgeNum,edgeNum1;//head1[],edgeNum1,edge1[]是缩点后的图 10 int cnt,scnt,begin,n,m;//begin是栈顶 11 int low[M],dfn[M],stack[M],id[M]; 12 int oppo[M],in[M],col[M];//in[]是拓扑排序的入度,oppo[]记录互相矛盾的点 13 bool instack[M],ans[M];//instack[]标志是否在栈中,ans[]标记是否在新娘这边 14 struct edge{int v,next;}edge[N],edge1[N]; 15 void add(int a,int b){ 16 edge[edgeNum].v=b; 17 edge[edgeNum].next=head[a]; 18 head[a]=edgeNum++; 19 } 20 void add1(int a,int b){ 21 edge1[edgeNum1].v=b; 22 edge1[edgeNum1].next=head1[a]; 23 head1[a]=edgeNum1++; 24 } 25 void dfs(int x){ 26 low[x]=dfn[x]=++cnt; 27 stack[++begin]=x; 28 instack[x]=1; 29 int v; 30 for(int i=head[x];i!=-1;i=edge[i].next){ 31 v=edge[i].v; 32 if(!dfn[v]){ 33 dfs(v); 34 low[x]=low[v]<low[x]?low[v]:low[x]; 35 }else if(instack[v]&&dfn[v]<low[x]){ 36 low[x]=dfn[v]; 37 } 38 } 39 if(low[x]==dfn[x]){ 40 scnt++; 41 do{ 42 v=stack[begin--]; 43 instack[v]=0; 44 id[v]=scnt; 45 }while(v!=x); 46 } 47 return ; 48 } 49 bool solve(int n){ 50 cnt=scnt=begin=0; 51 memset(dfn,0,sizeof(dfn)); 52 memset(instack,0,sizeof(instack)); 53 for(int i=0;i<2*n;i++){ 54 if(!dfn[i]) dfs(i); 55 } 56 for(int i=0;i<n;i++){ 57 //若有同一组的分量在同一个强连通分量中,则直接返回0 58 if(id[i]==id[i+n]) return 0; 59 //存储同一组的分量所在哪个强连通分量,这样访问过其中一个后,另一个就不用再去访问了 60 oppo[id[i]]=id[i+n]; 61 //在缩点的图中标记互斥的缩点。(原来互斥,现在也互斥) 62 oppo[id[i+n]]=id[i]; 63 } 64 memset(in,0,sizeof(in)); 65 memset(col,0,sizeof(col)); 66 //缩点后建立图,add1(a,b)增加边 67 for(int i=0;i<2*n;i++) 68 for(int j=head[i];j!=-1;j=edge[j].next){ 69 int v=edge[j].v; 70 if(id[i]!=id[v]){ 71 in[id[i]]++; 72 add1(id[v],id[i]); 73 } 74 } 75 //拓扑排序 76 queue<int> que; 77 for(int i=0;i<=scnt;i++){ 78 if(!in[i]) que.push(i); 79 } 80 while(!que.empty()){ 81 int now=que.front(); 82 que.pop(); 83 if(col[now]==0){//对于未着色的点x,将x染成红色1,同时将与x矛盾的点oppo[x]染成蓝色-1。 84 col[now]=1; 85 col[oppo[now]]=-1; 86 } 87 for(int i=head1[now];i!=-1;i=edge1[i].next){ 88 int v=edge1[i].v; 89 if(--in[v]==0) que.push(v); 90 } 91 } 92 memset(ans,0,sizeof(ans)); 93 for(int i=0;i<n;i++){ 94 if(col[id[i]]==1) ans[i]=1; 95 } 96 return 1; 97 } 98 int main(){ 99 int a,b; 100 char c,d; 101 while(scanf("%d%d",&n,&m)!=EOF){ 102 if(!n&&!m) break; 103 edgeNum=edgeNum1=0; 104 memset(in,0,sizeof(in)); 105 memset(id,0,sizeof(id)); 106 memset(head,-1,sizeof(head)); 107 memset(head1,-1,sizeof(head1)); 108 while(m--){ 109 scanf("%d%c %d%c",&a,&c,&b,&d); 110 if(c=='h'&&d=='h') {add(b+n,a);add(a+n,b);} 111 if(c=='h'&&d=='w') {add(a+n,b+n);add(b,a);} 112 if(c=='w'&&d=='h') {add(a,b);add(b+n,a+n);} 113 if(c=='w'&&d=='w') {add(a,b+n);add(b,a+n);} 114 } 115 add(0,0+n);//增加新娘到新郎的边 116 if(solve(n)){ 117 for(int i=1;i<n;i++){ 118 if(ans[i]) printf("%dh ",i); 119 else printf("%dw ",i); 120 } 121 printf("\n"); 122 } 123 else puts("bad luck"); 124 } 125 return 0; 126 }