题目描述
给定有向图 G=(V,E) G = ( V , E ) 。设 P P 是的一个简单路(顶点不相交)的集合。如果 V V 中每个顶点恰好在的一条路上,则称 P P 是的一个路径覆盖。 P P 中路径可以从 的任何一个顶点开始,长度也是任意的,特别地,可以为0。 G G 的最小路径覆盖是 的所含路径条数最少的路径覆盖。设计一个有效算法求一个有向无环图 G G 的最小路径覆盖。
输入格式:
件第1 行有2个正整数和 m m 。是给定有向无环图 G G 的顶点数,是 G G 的边数。接下来的行,每行有2 个正整数 i i 和,表示一条有向边 (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
说明
1≤n≤150,1≤m≤6000 1 ≤ n ≤ 150 , 1 ≤ m ≤ 6000
解:
这是一个网络流的套路,这玩意都能建网络流,orz,蒟蒻无话可说。
对于每一个点,它最多有两条边在路径当中,所以我们建网络流,点一分为二,在图中相连的点进行连边,汇点连1,源点连1,中间连 inf i n f 跑最大流。最大流的意义就是最小路径覆盖中连边的条数。我们用 n−flow(流量) n − f l o w ( 流 量 ) 就是答案。
画个图理解一下:
红色就代表了选择的连边, 1−2−3−4,5, 1 − 2 − 3 − 4 , 5 , 两条路。显然有多解,所以有spj。
#include<cstdio>
#include<iostream>
#include<cstring>
#include<queue>
using namespace std;
struct lxy{
int to,next,flow;
}b[50005];
int n,m,cnt=-1,head[505],s,t,num;
int layer[505];
int const inf=0x7f7f7f7f;
int to[155];bool vis[155];
int ans;
void add(int op,int ed,int flow)
{
b[++cnt].next=head[op];
b[cnt].to=ed;
b[cnt].flow=flow;
head[op]=cnt;
}
bool bfs()
{
memset(layer,0,sizeof(layer));
queue <int> d;
layer[s]=1;d.push(s);
while(!d.empty())
{
int now=d.front();
d.pop();
for(int i=head[now];i!=-1;i=b[i].next)
if(b[i].flow!=0&&layer[b[i].to]==0)
{
layer[b[i].to]=layer[now]+1;
d.push(b[i].to);
}
}
return layer[t];
}
int dfs(int u,int a)
{
if(a==0||u==t) return a;
int f,flow=0;
for(int i=head[u];i!=-1;i=b[i].next)
if(b[i].flow!=0&&layer[u]+1==layer[b[i].to])
{
f=dfs(b[i].to,min(a,b[i].flow));
b[i].flow-=f;
b[i^1].flow+=f;
flow+=f;
a-=f;
if(a==0) break;
}
return flow;
}
int dinic()
{
int ans=0;
while(bfs())
ans+=dfs(s,inf);
return ans;
}
int main()
{
scanf("%d%d",&n,&m);t=n*2+1;num=n;
for(int i=0;i<=n*2+1;i++) head[i]=-1;
for(int i=1;i<=m;i++)
{
int x,y;scanf("%d%d",&x,&y);
add(x,y+n,1);add(y+n,x,0);
}
for(int i=1;i<=n;i++) add(s,i,1),add(i,s,0);
for(int i=n+1;i<=n*2;i++) add(i,t,1),add(t,i,0);
ans=num-dinic();
for(int i=1;i<=n;i++)
{
for(int j=head[i];j!=-1;j=b[j].next)
if(b[j].flow==0)
to[i]=b[j].to-n;
}
for(int i=1;i<=n;i++)
if(vis[i]==0)
{
int p=i;
while(p!=-n)
{
vis[p]=1;
printf("%d ",p);
p=to[p];
}
printf("\n");
}
printf("%d",ans);
}