题意:有n对夫妇第i对夫妇表示为i-1h、i-1w。每对夫妇必须坐在桌子的不同侧。现在有m对通奸关系,新娘(0号妻子)不想看到她对面的任意两个人有通奸关系。求一种满足的坐法并输出坐新娘同侧的人。
分析:因为每对夫妻要么夫左妻右要么夫右妻左,所以很明显是一道2-SAT问题。
建图:设i'为第i个人的伴侣。对于某对通奸关系i和j,则连边i->j',j->i'。最后从新娘连一条边指向新郎以至于必须选新郎。
输出方法是,对原图求一次强连通分量,然后看每组中的两个点是否属于同一个强连通分量,如果存在这种情况,那么无解
然后对于缩点后的图G',我们将G'中所有边转置。进行拓扑排序。
对于缩点后的所有点,我们先预处理求出所有冲突顶点。例如缩点后Ai所在强连通分支的ID
为id[ Ai] ,同理~Ai在 id[ ~Ai ],所以冲突顶点
conflict[ id[Ai]]=conflict[ id[~Ai] ];
同理conflict[ id[~Ai]]=conflict[ id[Ai] ];
设缩点后有Nscc个点。
然后对拓扑序进行染色,初始化所有点color均为未着色
顺序遍历得到的拓扑序列,对于未着色的点x,将x染成红色,同时将所有与x矛盾的点conflic[x]染成蓝色。
2-sat的一组解就等价于所有缩点后点颜色为红色的点,也就是color[ id[i] ]=RED的所有点
唉调了半个早上最后才发现原来是要输出跟新娘坐同侧的~~
话说输出方案真的好麻烦,期望以后在比赛中不要遇到这样的题。
代码:
var n,m,e,d,sum,tot:longint; last,belong,color,dfn,low,con,du,stack,state:array[1..400] of longint; f:array[1..400] of boolean; side:array[1..200000] of record x,y,next:longint; end; function next(x:longint):longint; begin if x>n then exit(x-n) else exit(x+n); end; procedure add(x,y:longint); begin inc(e); side[e].x:=x; side[e].y:=y; side[e].next:=last[x]; last[x]:=e; end; function min(x,y:longint):longint; begin if x<y then exit(x) else exit(y); end; procedure init; var c:char; s:string; x,y,i:longint; begin fillchar(last,sizeof(last),0); e:=0; for i:=1 to m do begin read(c); s:=''; while c in ['0'..'9'] do begin s:=s+c; read(c); end; val(s,x); inc(x); if c='w' then x:=x+n; read(c); read(c); s:=''; while c in ['0'..'9'] do begin s:=s+c; read(c); end; val(s,y); inc(y); if c='w' then y:=y+n; readln; add(x,next(y)); add(y,next(x)); end; add(1+n,1); end; procedure dfs(x:longint); var i:longint; begin inc(d); dfn[x]:=d; low[x]:=d; inc(tot); stack[tot]:=x; f[x]:=true; i:=last[x]; while i>0 do with side[i] do begin if dfn[y]=0 then begin dfs(y); low[x]:=min(low[x],low[y]); end else if f[y] then low[x]:=min(low[x],dfn[y]); i:=next; end; if dfn[x]=low[x] then begin inc(sum); repeat i:=stack[tot]; dec(tot); f[i]:=false; belong[i]:=sum; until i=x; end; end; procedure tarjan; var i:longint; begin fillchar(dfn,sizeof(dfn),0); fillchar(low,sizeof(low),0); fillchar(f,sizeof(f),false); sum:=0; tot:=0; d:=0; for i:=1 to n*2 do if dfn[i]=0 then dfs(i); end; procedure topsort; var head,tail,i:longint; begin head:=0; tail:=0; for i:=1 to sum do if du[i]=0 then begin inc(tail); state[tail]:=i; end; repeat inc(head); i:=last[state[head]]; while i>0 do with side[i] do begin dec(du[y]); if du[y]=0 then begin inc(tail); state[tail]:=y; end; i:=next; end; until head>=tail; end; procedure dfs_blue(x:longint); var i:longint; begin color[x]:=-1; i:=last[x]; while i>0 do with side[i] do begin if color[y]=0 then dfs_blue(y); i:=next; end; end; procedure dfs_red; var i:longint; begin for i:=1 to sum do if color[state[i]]=0 then begin color[state[i]]:=1; dfs_blue(con[state[i]]); end; end; procedure print; var u,i:longint; begin for i:=2 to n do begin if i>2 then write(' '); if color[belong[i]]=-1 then write(i-1,'h') else write(i-1,'w'); end; writeln; end; procedure work; var i:longint; begin for i:=1 to n do begin if belong[i]=belong[i+n] then begin writeln('bad luck'); exit; end; con[belong[i]]:=belong[i+n]; con[belong[i+n]]:=belong[i]; end; fillchar(last,sizeof(last),0); fillchar(du,sizeof(du),0); for i:=1 to e do with side[i] do if belong[x]<>belong[y] then begin add(belong[y],belong[x]); inc(du[belong[x]]); end; topsort; fillchar(color,sizeof(color),0); dfs_red; print; end; begin readln(n,m); while n+m>0 do begin init; tarjan; work; readln(n,m); end; end.