2-SAT,直接选择新娘一侧的比较难做,所以处理的时候选择新郎一侧的,最后反着输出就可以。
A和B通奸的话,就建边 A->B'以及B->A’,表示 A在新郎一侧的话,B一定不在;B在新郎一侧的话,A一定不在。
然后再把新郎的mark标记为1,表示新郎一定选择。
#include<cstdio> #include<cstring> #include<cmath> #include<vector> #include<algorithm> using namespace std; const int maxn=8000+5; int N,m; char s1[10],s2[10]; struct TwoSAT { int n; vector<int> G[maxn*2]; bool mark[maxn*2]; int S[maxn*2],c; bool dfs(int x) { if(mark[x^1]) return false; if(mark[x]) return true; mark[x]=true; S[c++]=x; for(int i=0;i<G[x].size();i++) if(!dfs(G[x][i])) return false; return true; } void init(int n) { this->n=n; for(int i=0;i<n*2;i++) G[i].clear(); memset(mark,0,sizeof mark); } void add_clause(int x,int y) { G[x].push_back(y^1); G[y].push_back(x^1); } bool solve() { for(int i=0;i<2*n;i+=2) if(!mark[i]&&!mark[i+1]) { c=0; if(!dfs(i)) { while(c>0) mark[S[--c]]=false; if(!dfs(i+1)) return false; } } return true; } //输出字典序最小的解 void Printf() { for(int i=1;i<n;i++) { if(mark[2*i]) printf("%dw",i); else printf("%dh",i); if(i<n-1) printf(" "); else printf(" "); } } }; int main() { TwoSAT T; while(~scanf("%d%d",&N,&m)) { if(N==0&&m==0) break; T.n=N; T.init(T.n); for(int i=0;i<m;i++) { int a=0,b=0; scanf("%s%s",s1,s2); for(int j=0;j<strlen(s1)-1;j++) a=a*10+s1[j]-'0'; for(int j=0;j<strlen(s2)-1;j++) b=b*10+s2[j]-'0'; if(s1[strlen(s1)-1]=='h') a=a*2; else a=a*2+1; if(s2[strlen(s2)-1]=='h') b=b*2; else b=b*2+1; T.add_clause(a,b); } T.mark[0]=1; if(T.solve()) T.Printf(); else printf("bad luck "); } return 0; }