poj 3648 Wedding 2-SAT输出

题意:有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.


你可能感兴趣的:(poj 3648 Wedding 2-SAT输出)