问题描述
给定有向图 G=(V,E)。设 P 是 G 的一个简单路(顶点不相交)的集合。如果 V 中每个顶点恰好在 P 的一条路上,则称 P是 G 的一个路径覆盖。
P 中路径可以从 V 的任何一个顶点开始,长度也是任意的,特别地,可以为0。G 的最小路径覆盖是G 的所含路径条数最少的路径覆盖。
设计一个有效算法求一个有向无环图G 的最小路径覆盖。
提示:设V={1,2,¼ ,n},构造网络 G1=(V1,E1)如下:
V
1
={
x0,x1,⋅⋅⋅,xn
}
∪
{
y0,y1,⋅⋅⋅,yn
},
E
1
={
(x0,xi):i∈V
}
∪
{
yi,y0:i∈V
}
∪
{
(xi,yj):(i.j)∈E
}
每条边的容量均为1。求网络G1 的(x0,y0)最大流。
编程任务
对于给定的给定有向无环图G,编程找出 G的一个最小路径覆盖。
数据输入
由文件input.txt提供输入数据。文件第1 行有 2个正整数 n和 m。n是给定有向无环图G 的顶点数, m是G 的边数。 接下来的 m行, 每行有 2 个
正整数 i和 j, 表示一条有向边(i,j)。
结果输出
程序运行结束时,将最小路径覆盖输出到文件 output.txt 中。从第 1 行开始,每行输出
一条路径。文件的最后一行是最少路径数。
输入文件示例
input.txt
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
输出文件示例
output.txt
1 4 7 10 11
2 5 8
3 6 9
3
题解
变态的一道网络流题目,欺骗我感情
好吧,这是一道DAG最短路径覆盖问题。将原来的点进行拆点,对于原来的边(x,y),在网络流图中连接(
x0,y1
),形成一个二分图,再进行dinic,用节点数n减去所得结果就是路径条数。
为了输出方案,我们可以定义一个数组next,对于每一个节点,我们在dfs的时候记录它的下一个节点,存储到next数组中,最终输出的时候用while循环即可。
到网上膜了一圈,感觉黄学长写得非常清楚,值得参考一下。
CODE:
#include<cstdio>
#include<cstring>
const int INF=1e9;
struct queue
{
int h,t;
int a[500];
inline void clear(){h=1,t=0;}
inline void push(int n){a[++t]=n;}
inline int front(){return a[h];}
inline void pop(){h++;}
inline bool empty(){return h>t;}
}q;
struct edge
{
int next,to,remain;
}a[20000];
int head[500];
int deep[500];
int next[500];
int n,m,S,T,num=1,ans,x,y;
inline int min(int a,int b){return a<b?a:b;}
inline void add(int x,int y,int cap)
{
a[++num].next=head[x],a[num].to=y,a[num].remain=cap,head[x]=num;
a[++num].next=head[y],a[num].to=x,head[y]=num;
}
inline bool bfs()
{
memset(deep,0x3f,sizeof(deep));
q.clear(),q.push(S);
deep[S]=0;
while(!q.empty())
{
int tmp=q.front();q.pop();
for(int i=head[tmp];i;i=a[i].next)
if(deep[a[i].to]>INF&&a[i].remain)
q.push(a[i].to),deep[a[i].to]=deep[tmp]+1;
}
return deep[T]<INF;
}
int dfs(int now,int limit)
{
if(now==T||!limit) return limit;
int flow=0,f;
for(int i=head[now];i;i=a[i].next)
if(a[i].remain&&deep[a[i].to]==deep[now]+1&&(f=dfs(a[i].to,min(limit,a[i].remain))))
{
next[now]=a[i].to;
flow+=f,limit-=f,a[i].remain-=f,a[i^1].remain+=f;
if(!limit) return flow;
}
deep[now]=-1;
return flow;
}
inline int dinic()
{
int ans=0;
while(bfs()) ans+=dfs(S,INF);
return ans;
}
void print(int now)
{
while(next[now])
{
printf("%d ",now);
now=next[now]-n;
}
printf("%d\n",now);
}
int main()
{
scanf("%d%d",&n,&m);
//1~n:顶点 n+1~2*n:拆点 2*n+1:源点 2*n+2:汇点
for(int i=1;i<=m;i++)
scanf("%d%d",&x,&y),add(x,y+n,1);
S=n<<1|1,T=S+1;
for(int i=1;i<=n;i++)
add(S,i,1);
for(int i=1;i<=n;i++)
add(n+i,T,1);
ans=dinic();
for(int i=1;i<=n;i++)
if(!next[n+i]) print(i);
printf("%d",n-ans);
return 0;
}