bzoj 1143 && bzoj 2718 最长反链

4 篇文章 0 订阅

题意:n个点,m条有向边,求最长反链

又到了涨姿势的时候了...(果然蒟蒻)

DAG中,有如下的一些定义和性质:

链:一条链是一些点的集合,链上任意两个点x, y,满足要么 x 能到达 y ,要么 y 能到达 x 。

反链:一条反链是一些点的集合,链上任意两个点x, y,满足 x 不能到达 y,且 y 也不能到达 x。

一个定理:最长反链长度 = 最小链覆盖(用最少的链覆盖所有顶点)

对偶定理:最长链长度 = 最小反链覆盖                                                                  

那么我们要求出的就是这个有向无环图的最小链覆盖了。

最小链覆盖即路径可以相交的最小路径覆盖

先看路径不能相交的最小路径覆盖怎么来做:

建立一个二分图,两边都是n个点,原图的每个点 i 对应两个,在左边的编号 i, 在右边的编号 n+i。

原图中如果存在一条边 (x, y),那么就在二分图中连一条 (x1, y2) 的边。

则 原图的最小路径覆盖(路径不能相交)= n-二分图最大匹配

那么路径可以相交的最小路径覆盖(最小链覆盖)呢?

将原图做一次Floyd传递闭包,得出x是否能到达y

如果两个点 x, y,满足 x 可以到达 y ,那么就在二分图中建立边 (x1, y2) 。

这里与上面不同,只要 x 能到达 y ,就直接连一条边 (x, y)

然后就转化为了路径不能相交的最小路径覆盖了。

原图的最小链覆盖 = n -二分图最大匹配


bzoj 1143

var
        n,m,x,y,ans,l   :longint;
        i,j             :longint;
        map             :array[0..110,0..110] of boolean;
        link,last       :array[0..210] of longint;
        flag            :array[0..210] of boolean;
        pre,other       :array[0..10010] of longint;

procedure floyd;
var
        i,j,k:longint;
begin
   for k:=1 to n do
     for i:=1 to n do
       for j:=1 to n do
         map[i,j]:=(map[i,k] and map[k,j]) or map[i,j];
end;

procedure connect(x,y:longint);
begin
   inc(l);
   pre[l]:=last[x];
   last[x]:=l;
   other[l]:=y;
end;

function find(x:longint):boolean;
var
    p,q:longint;
begin
   q:=last[x];
   while (q<>0) do
   begin
      p:=other[q];
      if not flag[p] then
      begin
         flag[p]:=true;
         if (link[p]=0) or (find(link[p])) then
         begin
            link[p]:=x;
            exit(true);
         end;
      end;
      q:=pre[q];
   end;
   exit(false);
end;

begin
   read(n,m);
   for i:=1 to m do
   begin
      read(x,y);
      map[x,y]:=true;
   end;
   floyd;
   for i:=1 to n do
     for j:=1 to n do
       if map[i,j] then connect(i,n+j);
   ans:=0;
   for i:=1 to n do
   begin
      fillchar(flag,sizeof(flag),false);
      if find(i) then inc(ans);
   end;
   ans:=n-ans;
   writeln(ans);
end.


bzoj 2718

var
        n,m,x,y,ans,l   :longint;
        i,j             :longint;
        map             :array[0..210,0..210] of boolean;
        pre,other       :array[0..40010] of longint;
        flag            :array[0..410] of boolean;
        last,link       :array[0..410] of longint;
procedure connect(x,y:longint);
begin
   inc(l);
   pre[l]:=last[x];
   last[x]:=l;
   other[l]:=y;
end;

procedure floyd;
var
        i,j,k:longint;
begin
   for k:=1 to n do
     for i:=1 to n do
       for j:=1 to n do
         map[i,j]:=map[i,j] or (map[i,k] and map[k,j]);
end;

function find(x:Longint):boolean;
var
        p,q:longint;
begin
   q:=last[x];
   while (q<>0) do
   begin
      p:=other[q];
      if not flag[p] then
      begin
         flag[p]:=true;
         if (link[p]=0) or find(link[p]) then
         begin
            link[p]:=x;
            exit(true);
         end;
      end;
      q:=pre[q];
   end;
   exit(false);
end;

begin
   read(n,m);
   for i:=1 to m do
   begin
      read(x,y);
      map[x,y]:=true;
   end;
   floyd;
   for i:=1 to n do
     for j:=1 to n do
       if map[i,j] then connect(i,n+j);
   //
   ans:=0;
   for i:=1 to n do
   begin
      fillchar(flag,sizeof(flag),false);
      if find(i) then inc(ans);
   end;
   writeln(n-ans);
end.
——by Eirlys


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值