poj 3648 Wedding 【2-sat 经典建图 输出一组可行解 好题】 【tarjan求SCC + 缩点 + 拓扑排序 + 染色】

来源:互联网 发布:剪切合并音乐软件 编辑:程序博客网 时间:2024/05/19 13:20
Wedding
Time Limit: 1000MS Memory Limit: 65536KTotal Submissions: 8891 Accepted: 2678 Special Judge

Description

Up to thirty couples will attend a wedding feast, at which they will be seated on either side of a long table. The bride and groom sit at one end, opposite each other, and the bride wears an elaborate headdress that keeps her from seeing people on the same side as her. It is considered bad luck to have a husband and wife seated on the same side of the table. Additionally, there are several pairs of people conducting adulterous relationships (both different-sex and same-sex relationships are possible), and it is bad luck for the bride to see both members of such a pair. Your job is to arrange people at the table so as to avoid any bad luck.

Input

The input consists of a number of test cases, followed by a line containing 0 0. Each test case gives n, the number of couples, followed by the number of adulterous pairs, followed by the pairs, in the form "4h 2w" (husband from couple 4, wife from couple 2), or "10w 4w", or "3h 1h". Couples are numbered from 0 to n - 1 with the bride and groom being 0w and 0h.

Output

For each case, output a single line containing a list of the people that should be seated on the same side as the bride. If there are several solutions, any one will do. If there is no solution, output a line containing "bad luck".

Sample Input

10 63h 7h5w 3w7h 6w8w 3w7h 3w2w 5h0 0

Sample Output

1h 2h 3w 4h 5h 6h 7h 8h 9h


题意:很多对夫妇参加一对新人的婚礼。分别做在长桌子的两侧。新郎、新娘分别坐两侧,新娘只能看到她对面的人。新娘不想看到她对面有夫妇。

而且有一些人是有通奸关系的(男男,男女、女女都可能有,而且新郎也可能和别人有通奸关系),新娘不想看到有通奸关系的一对人坐在她对面。


转化问题:我们需要选出包括新郎在内的一半人坐在新娘对面去,选出来的人不能有夫妇,不能有通奸关系的一对人。可以选择编号第i对夫妇中妻子为

2*i,丈夫为2*i+1。那么则有新郎为1,新娘为0;


建图:对于 a op1  b op2

一:a男 b男

1,addEdge(2*a + 1, 2*b);//选了a男到新娘对面  也必选b男妻子 
2,addEdge(2*b + 1, 2*a);//选了b男到新娘对面  也必选a男妻子  

二:a男 b女 

1,addEdge(2*a + 1, 2*b + 1);//选了a男到新娘对面 也必选b女丈夫 
2,addEdge(2*b, 2*a);//选了b女到新娘对面  也必选a男妻子 

三:a女 b男

1,addEdge(2*a, 2*b);//选了a女到新娘对面  也必选b男妻子 
2,addEdge(2*b + 1, 2*a + 1);//选了b男到新娘对面  也必选a女丈夫 


四:a女 b女

1,addEdge(2*a, 2*b + 1);//选了a女到新娘对面  也必选b女丈夫 
2,addEdge(2*b, 2*a + 1);//选了b女到新娘对面  也必选a女丈夫 


最最重要的是我们必需选择新郎,加一条0->1的边,表示必选1。看代码注释


注意的是输出的时候是输出和新娘坐同一边的人。即反过来输出就可以了;

#include <cstdio>#include <cstring>#include <queue>#include <vector>#include <stack>#include <algorithm>#define MAXN 100#define MAXM 100000#define INF 10000000#define eps 1e-5using namespace std;struct Edge{int from, to, next;}edge[MAXM];//原图int head[MAXN], edgenum; vector<int> G[MAXN];//缩点后新图 int in[MAXN];//新图SCC的入度 int low[MAXN], dfn[MAXN];int dfs_clock;int sccno[MAXN], scc_cnt;stack<int> S;bool Instack[MAXN];int N, M;int color[MAXN];//染色 int fp[MAXN];//建立对应SCC编号的映射 void init(){edgenum = 0;memset(head, -1, sizeof(head)); }void addEdge(int u, int v){Edge E = {u, v, head[u]};edge[edgenum] = E;head[u] = edgenum++;} void getMap()//选择N个人到新娘对面 {addEdge(0, 1);//表示必选新郎   因为选择新娘必选新郎 -> 矛盾 int a, b;char op1, op2;while(M--){getchar();scanf("%d%c %d%c", &a, &op1, &b, &op2);if(op1 == 'h' && op2 == 'h')//a男 b男 {addEdge(2*a + 1, 2*b);//选了a男到新娘对面  也必选b男妻子 addEdge(2*b + 1, 2*a);//选了b男到新娘对面  也必选a男妻子  } else if(op1 == 'h' && op2 == 'w')//a男 b女 {addEdge(2*a + 1, 2*b + 1);//选了a男到新娘对面 也必选b女丈夫 addEdge(2*b, 2*a);//选了b女到新娘对面  也必选a男妻子 }else if(op1 == 'w' && op2 == 'h')//a女 b男 {addEdge(2*a, 2*b);//选了a女到新娘对面  也必选b男妻子 addEdge(2*b + 1, 2*a + 1);//选了b男到新娘对面  也必选a女丈夫 }else//a女 b女 {addEdge(2*a, 2*b + 1);//选了a女到新娘对面  也必选b女丈夫 addEdge(2*b, 2*a + 1);//选了b女到新娘对面  也必选a女丈夫 }}} void tarjan(int u, int fa){int v;low[u] = dfn[u] = ++dfs_clock;S.push(u);Instack[u] = true;for(int i = head[u]; i != -1; i = edge[i].next){v = edge[i].to;if(!dfn[v]){tarjan(v, u);low[u] = min(low[u], low[v]);}else if(Instack[v])low[u] = min(low[u], dfn[v]);}if(low[u] == dfn[u]){scc_cnt++;for(;;){v = S.top(); S.pop();Instack[v] = false;sccno[v] = scc_cnt;if(v == u) break;}}}void find_cut(int l, int r){memset(low, 0, sizeof(low));memset(dfn, 0, sizeof(dfn));memset(sccno, 0, sizeof(sccno));memset(Instack, false, sizeof(Instack));dfs_clock = scc_cnt = 0;for(int i = l; i <= r; i++)if(!dfn[i]) tarjan(i, -1);}void suodian(){for(int i = 1; i <= scc_cnt; i++) G[i].clear(), in[i] = 0;for(int i = 0; i < edgenum; i++){int u = sccno[edge[i].from];int v = sccno[edge[i].to];if(u != v)G[v].push_back(u), in[u]++; } }void toposort(){queue<int> Q;memset(color, 0, sizeof(color));for(int i = 1; i <= scc_cnt; i++) if(in[i] == 0) Q.push(i);while(!Q.empty()){int u = Q.front();Q.pop();if(color[u] == 0){color[u] = 1;color[fp[u]] = 2;}for(int i = 0; i < G[u].size(); i++){int v = G[u][i];if(--in[v] == 0)//入度减一 Q.push(v);}}}void solve(){memset(fp, 0, sizeof(fp));for(int i = 0; i < N; i++){if(sccno[2*i] == sccno[2*i + 1]){printf("bad luck\n");return ;}else{fp[sccno[2*i]] = sccno[2*i + 1];fp[sccno[2*i + 1]] = sccno[2*i];}}suodian();//缩点 toposort();//拓扑排序 + 染色 for(int i = 1; i < N; i++){if(color[sccno[2*i]] == 1)//选了妻子去对面 printf("%dh ", i);//输出对应丈夫 elseprintf("%dw ", i);//同上 }printf("\n");}int main(){while(scanf("%d%d", &N, &M), N||M){init();getMap();find_cut(1, 2*N-1);solve(); }return 0;}


0 0