您的位置:首页 > 理论基础 > 计算机网络

P2764 最小路径覆盖问题(网络流)

2018-03-27 07:55 381 查看

题目描述

给定有向图G=(V,E)G=(V,E)。设PP是GG的一个简单路(顶点不相交)的集合。如果VV中每个顶点恰好在PP的一条路上,则称PP是GG的一个路径覆盖。PP 中路径可以从VV 的任何一个顶点开始,长度也是任意的,特别地,可以为0。GG 的最小路径覆盖是GG 的所含路径条数最少的路径覆盖。设计一个有效算法求一个有向无环图GG 的最小路径覆盖。

输入格式:

件第1 行有2个正整数nn和mm。nn是给定有向无环图GG 的顶点数,mm是GG 的边数。接下来的mm行,每行有2 个正整数ii和jj,表示一条有向边(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≤60001≤n≤150,1≤m≤6000

传送门!!!

解:

这是一个网络流的套路,这玩意都能建网络流,orz,蒟蒻无话可说。

对于每一个点,它最多有两条边在路径当中,所以我们建网络流,点一分为二,在图中相连的点进行连边,汇点连1,源点连1,中间连infinf跑最大流。最大流的意义就是最小路径覆盖中连边的条数。我们用n−flow(流量)n−flow(流量)就是答案。

画个图理解一下:



红色就代表了选择的连边,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);
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: