poj 2723 Get Luffy Out 2_SAT

题目大意

有m层楼,从一层到m层,要进入每层都要打开位于该层的两道门中的至少一道。门锁有2n种,每个门锁为2n种中的一种,可以重复。有2n把钥匙,分别对应2n种锁,但是钥匙两两一组,共n组,每组只能选一个来开门,被选中的可以多次使用,另一个一次都不能用。问最多能上多少层。

 

分析

  对于每组钥匙,只能二取一,所以是2-SAT模型。

  建图:某层有x,y两种锁,x的钥匙a与钥匙b一组,y的要是c与钥匙d一组。如果在某次选了钥匙b,那么本层的x将无法被打开,只能开y,就必须选钥匙c,不能选钥匙d(因为钥匙d解不开y)。链接(b,c)和(d,a)。

  答案二分

 

代码

type
  arr=record
    x,y,next:longint;
end;

var
  a:array[1..2000000] of arr;
  f:array[1..3000] of boolean;
  ls,zan,g:array[1..3000] of longint;
  dfn,low,num1,x1,y1:array[1..3000] of longint;
  tot,sum:longint;
  n,m:longint;
  i,j,k:longint;
  r,l,mid:longint;
  ans:longint;
  e,d:longint;
  x,y:longint;

procedure add(x,y:longint);
begin
  inc(e);
  a[e].x:=x;
  a[e].y:=y;
  a[e].next:=ls[x];
  ls[x]:=e;
end;

function min(x,y:longint):longint;
begin
  if x<y then exit(x)
         else exit(y);
end;

procedure dfs(x:longint);
var
  i:longint;
begin
  inc(d);
  dfn[x]:=d;
  low[x]:=d;
  inc(tot);
  zan[tot]:=x;
  f[x]:=true;
  i:=ls[x];
  while i>0 do
    with a[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 low[x]=dfn[x] then
  begin
    inc(sum);
    repeat
      i:=zan[tot];
      dec(tot);
      f[i]:=false;
      g[i]:=sum;
    until i=x;
  end;
end;

procedure tarjan;
var
  i:longint;
begin
  for i:=1 to n*2 do
    if dfn[i]=0
      then
        dfs(i);
end;

function work:boolean;
var
  i:longint;
begin
  for i:=1 to n do
    if g[i]=g[i+n] then
    begin
      work:=false;
      exit;
    end;
  exit(true);
end;

function ban(x:longint):longint;
begin
  if x>n then exit(x-n);
  exit(x+n);
end;

procedure init;
var
  i,j,k:longint;
begin
  fillchar(x1,sizeof(x1),0);
  fillchar(y1,sizeof(y1),0);
  fillchar(num1,sizeof(num1),0);
  for i:=1 to n do
    begin
      readln(x,y);
      x:=x+1; y:=y+1;
      num1[x]:=i;
      num1[y]:=i+n;
    end;
  for i:=1 to m do
    begin
      readln(x,y);
      x:=x+1; y:=y+1;
      x1[i]:=num1[x];
      y1[i]:=num1[y];
    end;
end;

begin
  readln(n,m);
  while (n<>0) or (m<>0) do begin
  init;
  l:=0; r:=m;
  ans:=0;
  while l<=r do
    begin
      mid:=(l+r) div 2;
      fillchar(dfn,sizeof(dfn),0);
      fillchar(low,sizeof(low),0);
      fillchar(ls,sizeof(ls),0);
      fillchar(g,sizeof(g),0);
      fillchar(zan,sizeof(zan),0);
      fillchar(f,sizeof(f),0);
      e:=0; d:=0; tot:=0; sum:=0;
      for i:=1 to mid do
        begin
          add(ban(x1[i]),y1[i]);
          add(ban(y1[i]),x1[i]);
        end;
      tarjan;
      if work
        then
          begin
            l:=mid+1;
            if ans<mid then ans:=mid;
          end
        else
          r:=mid-1;
    end;
  writeln(ans);
  readln(n,m);
  end;
end.


posted @ 2016-06-14 20:59  一个响亮的蒟蒻  阅读(82)  评论(0编辑  收藏  举报