2-sat 基本是有三类题型,一种只判定解是否存在,一种判定+二分答案求最佳,一种判定解并输出其中一组解。
Wedding 这题是典型的第三种类型。
ps. 最近太忙了,这题是前天A的,暂时先要把 2-sat 放一放。然后到时候再回来整理下,形成要用到的模板带出去就行了,如果考到就是看临场建图了。
于是这题先记录下来。另外最后输出还是有点疑问的,mark一下,输出的新娘一边,是删除的scc点,还是选择的scc点,即 select[],是一定的吗?跟拓扑的第一个顺序有关?
AC代码:
//POJ-3648 Wedding 2-sat+topsort输出 //n对夫妻。 //i:妻子,i+n:丈夫, //i+2n:妻子',i+3n:丈夫'。 //原来我一直加错边了!! //输出的最后一个问题还是不明真相........... #include<cstdio> #include<cstring> #include<iostream> using namespace std; #define MAXN 150 //4倍的点 #define MAXM 20002 //n*n/2 struct node{ int u, v; }a[MAXM]; int first[MAXN], next[MAXM], idx; //idx 边下标 int n; //n对夫妻 int m; //m对奸情 node a2[MAXM]; int f2[MAXN], next2[MAXM], idx2; void add2(int u, int v) { a2[idx2].u=u, a2[idx2].v=v; next2[idx2]=f2[u]; f2[u]=idx2++; } void addedge(int u, int v) { a[idx].u = u, a[idx].v = v; next[idx] = first[u]; first[u] = idx++; } int dfn[MAXN], low[MAXN]; int stack[MAXN], ins[MAXN]; int belong[MAXN], cnt; //属于什么连通分量,cnt分量总数! int top, num; void tarjin(int u) { dfn[u] = low[u] = ++num; ins[u] = 1; stack[top++] = u; for(int e=first[u]; e!=-1; e=next[e]) { int v = a[e].v; if(!dfn[v]) { tarjin(v); low[u] = min(low[u], low[v]); } else if(ins[v]) { low[u] = min(low[u], dfn[v]); } } if(dfn[u] == low[u]) { while(1) { int v = stack[--top]; ins[v] = 0; belong[v] = cnt; //标记分量belong if(u == v) break; } cnt++; } } int pairs[MAXN]; //0/1对。不知道为什么很多模板用 fc[]这个名称呢? int select[MAXN]; //染色就染色嘛,尼玛就写个col看得我一头雾水...那我select表示选择 int solve() { for(int i=0; i<4*n; i++) //4*n 总点数 { if(!dfn[i]) tarjin(i); } for(int i=0; i<2*n; i++) //遍历 女、男 //枚举搭档. 0/1对 { if(belong[i] == belong[i+2*n]) //若有属于一个分量的。 { return 0; } //若不属于,则要记录一下scc的仇恨关系 pairs[belong[i]] = belong[i+2*n]; pairs[belong[i+2*n]] = belong[i]; } return 1; } int read() { memset(belong, 0, sizeof(belong)); memset(dfn, 0, sizeof(dfn)); memset(first, -1, sizeof(first)); memset(ins, 0, sizeof(ins)); num = cnt = 0; top = 0; idx = 0; //初始化边编号 if(scanf("%d%d", &n, &m)!=EOF && !(!n && !m)) { for(int i=0; i<n; i++) { addedge(i, i+3*n); addedge(i+3*n, i); addedge(i+n, i+2*n); addedge(i+2*n, i+n); } addedge(0+2*n, 0); //令新娘在左边。 左为选择。 for(int i=0; i<m; i++) { int a1, a2; char c1, c2; scanf("%d%c%d%c", &a1, &c1, &a2, &c2); if(c1=='h') a1+=n; if(c2=='h') a2+=n; //下标转化。女->男 //debug addedge(a1+2*n, a2); //!!!!!注意这里不要加反??若a1在对面,a2必须在左边(新娘同侧) addedge(a2+2*n, a1); // } return 1; } else return 0; } int vis[MAXN], topo[MAXN], tot; //tot为拓扑下标 void dfs(int u) //试一下用dfs来topsort, 再改成bfs试下!! { vis[u] = 1; for(int e=f2[u]; e!=-1; e=next2[e]) { int v = a2[e].v; if(!vis[v]) dfs(v); } if(select[u] == -1) //若u未染色,进行选择删除 { select[u] = 1; //1为选择 select[pairs[u]] = 0; //0为删除。 } topo[--tot] = u; } void topsort() { memset(select, -1, sizeof(select)); //select初始为-1 tot = cnt; //反向缩点图的点数 memset(vis, 0, sizeof(vis)); for(int i=0; i<cnt; i++) //cnt点总数。反向图中,下标为分量下标 if(!vis[i]) dfs(i); } void print() { idx2 = 0; memset(f2, -1, sizeof(f2)); for(int i=0; i<idx; i++) //缩点,建图 { if(belong[a[i].u]!=belong[a[i].v]) //原图的边,若不属于一个分量 { add2(belong[a[i].v], belong[a[i].u]); //加反向边,belong,分量值当下标。 //printf("insert(%d, %d)\n", belong[a[i].v], belong[a[i].u]); } } topsort(); for(int i=1; i<n; i++) //扫一边。女的 { if(i!=1) printf(" "); if(select[belong[i]] == 0) printf("%dw", i); //不是select=1的点,而是为0的?? else printf("%dh", i); } printf("\n"); } int main() { while(read()) { if(solve()) { // printf("YES\n"); print(); } else printf("bad luck\n"); } }