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