[BZOJ1143] CTSC2008 祭祀river floyed+二分图匹配

先用floyed判断两点是否联通。
把一个点v拆成vx,vy。如果i能到达j,那么连边(ix,jy)。
求最大点独立集,即最大匹配。
剩下的点都两两不联通了,于是答案就等于总点数-最大匹配。
二分图相关结论:
最小点覆盖(用最少的点覆盖所有的边)=最大匹配
最小边覆盖(用最少的边覆盖所有的点)=最大匹配+总点数-2*最大匹配=总点数-最大匹配
因为除了匹配边覆盖的点,剩下的点每个需要一条边覆盖。
最大点独立集(最大的两两不联通的点集)=总点数-最小点覆盖=总点数-最大匹配
因为删掉那些在最小点覆盖里的点以后,就已经删掉所有边了

代码

type
  edge=^edgenode;
  edgenode=record
    t:longint;
    next:edge;
  end;
var
  n,m,u,v,i,j,k,ans:longint;
  con:array[1..210]of edge;
  link:array[1..210]of longint;
  f:array[1..110,1..110]of boolean;
  visit:array[1..210]of boolean;
procedure ins(x,y:longint);
var
  p:edge;
begin
  new(P);
  p^.t:=y;
  p^.next:=con[x];
  con[x]:=p;
end;
function pipei(x:longint):boolean;
var
  p:edge;
begin
  pipei:=false;
  p:=con[x];
  visit[x]:=true;
  while p<>nil do
  begin
    if visit[p^.t]=false then
    begin
      visit[p^.t]:=true;
      if (link[p^.t]=-1)or(pipei(link[p^.t])=true) then
      begin
        link[p^.t]:=x;
        exit(true);
      end;
    end;
    p:=p^.next;
  end;
end;
begin
  readln(n,m);
  for i:=1 to m do
  begin
    readln(u,v);
    f[u,v]:=true;
  end;
  for k:=1 to n do
    for i:=1 to n do
      for j:=1 to n do
        f[i,j]:=f[i,j]or(f[i,k] and f[k,j]);
  for i:=1 to n do
    for j:=1 to n do
      if f[i,j]=true then begin  ins(i,j+n);  end;
  for i:=1 to n do
    link[i+n]:=-1;
  for i:=1 to n do
  begin
    fillchar(visit,sizeof(visit),false);
    if pipei(i)=true then inc(ans);
  end;
  writeln(n-ans);


end.   

你可能感兴趣的:(二分图)