问题描述:
给定有向图G=(V,E)。设P 是G 的一个简单路(顶点不相交)的集合。如果V 中每个顶点恰好在P 的一条路上,则称P是G 的一个路径覆盖。P 中路径可以从V 的任何一个顶点开始,长度也是任意的,特别地,可以为0。G 的最小路径覆盖是G 的所含路径条数最少的路径覆盖。设计一个有效算法求一个有向无环图G 的最小路径覆盖。提示:设V={1,2,…. ,n},构造网络G1=(V1,E1)如下:
每条边的容量均为1。求网络G1的( 0 x , 0 y )最大流。
«编程任务:
对于给定的给定有向无环图G,编程找出G的一个最小路径覆盖。
输入输出格式
输入格式:
件第1 行有2个正整数n和m。n是给定有向无环图G 的顶点数,m是G 的边数。接下来的m行,每行有2 个正整数i和j,表示一条有向边(i,j)。
输出格式:
从第1 行开始,每行输出一条路径。文件的最后一行是最少路径数。
输入输出样例
输入样例#1:
11 12
1 2
1 3
1 4
2 5
3 6
4 7
5 8
6 9
7 10
8 11
9 11
10 11
输出样例#1:
1 4 7 10 11
2 5 8
3 6 9
3
大意:
求一个图的最小点路径覆盖问题,要输出覆盖路径。
分析:
建立一个新图,将G中的每个点i在新图中拆成两个点i、i’,若G中存在边 < < i, j>则在新图中连边 < < i, j’ > > ,显然新图是一个二分图,求其最大匹配,则(N-新图最大匹配的值)就是最小点路径覆盖值。
输出路径从每个点开始,遍历一下,因为会跑到i的对点j’,因此下次应该从j开始搜索,如果一边的权为0,则有东西流过,就是解。
代码:
const
maxn=2003;
maxm=200003;
type
node=record
y,c,next,op:longint;
end;
var
e,n,m,s,t,ans,d,x,y,i,j,num:longint;
ls,dis,q,cur,path,v:array [0..maxn] of longint;
g:array [0..maxm*2] of node;
procedure add(u,v,c:longint);
begin
inc(e);
g[e].y:=v; g[e].c:=c; g[e].op:=e+1; g[e].next:=ls[u]; ls[u]:=e;
inc(e);
g[e].y:=u; g[e].c:=0; g[e].op:=e-1; g[e].next:=ls[v]; ls[v]:=e;
end;
function bfs:boolean;
var i,head,tail,tt,u:longint;
begin
for i:=s to t do
dis[i]:=0;
dis[s]:=1;
head:=0; tail:=1;
inc(tail);
q[tail]:=s;
while head<=tail do
begin
inc(head);
u:=q[head];
i:=ls[u];
while i>0 do
begin
if (g[i].c<>0) and (dis[g[i].y]=0) then
begin
dis[g[i].y]:=dis[u]+1;
if g[i].y=t then exit(true);
inc(tail);
q[tail]:=g[i].y;
end;
i:=g[i].next;
end;
end;
exit(false);
end;
function min(x,y:longint):longint;
begin
if xthen exit(x)
else exit(y);
end;
function dfs(x,maxf:longint):longint;
var ret,i,f:longint;
begin
if (x=t) or (maxf=0) then exit(maxf);
ret:=0;
i:=cur[x];
while i>0 do
begin
if (g[i].c<>0) and (dis[g[i].y]=dis[x]+1) then
begin
f:=dfs(g[i].y,min(g[i].c,maxf));
dec(g[i].c,f);
inc(g[g[i].op].c,f);
ret:=ret+f;
if ret=maxf then break;
end;
i:=g[i].next;
end;
exit(ret);
end;
procedure dinic;
var i:longint;
begin
while bfs do
begin
for i:=s to t do
cur[i]:=ls[i];
ans:=ans+dfs(s,maxlongint);
end;
end;
procedure find(x:longint);
var i,y:longint;
begin
if (x>2*n) or (x<0) then exit;
v[x]:=1;
inc(num);
path[num]:=x;
i:=ls[x];
while i>0 do
begin
y:=g[i].y;
if (v[y]=0) and (y>0) and (g[i].c=0) then
find(y-n);
i:=g[i].next;
end;
end;
begin
readln(n,m);
for i:=1 to m do
begin
readln(x,y);
add(x,y+n,1);
end;
for i:=1 to n do
add(0,i,1);
for i:=n+1 to 2*n do
add(i,2*n+1,1);
s:=0; t:=2*n+1;
fillchar(v,sizeof(v),0);
dinic;
for i:=1 to n do
begin
num:=0;
if v[i]=1 then continue;
fillchar(path,sizeof(path),0);
find(i);
for j:=1 to num do
write(path[j],' ');
writeln;
end;
writeln(n-ans);
end.