题目描述
给定有向图G=(V,E)。设P 是G 的一个简单路(顶点不相交)的集合。如果V 中每个顶点恰好在P 的一条路上,则称P是G 的一个路径覆盖。P 中路径可以从V 的任何一个顶点开始,长度也是任意的,特别地,可以为0。G 的最小路径覆盖是G 的所含路径条数最少的路径覆盖。设计一个有效算法求一个有向无环图G 的最小路径覆盖。提示:设V={1,2,…. ,n},构造网络G1=(V1,E1)如下:
每条边的容量均为1。求网络G1的(0x ,0y)最大流。
对于给定的给定有向无环图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
说明 1<=n<=150,1<=m<=6000
思路
这题是网络流中最大流的裸体
他所求的是最小路径覆盖,我们首先跑一遍最大流
求出最大匹配,n-最大匹配就是所求答案
我们在跑dinic时标记一下走过的点以及他连的点,就可得出每条路的路径覆盖
代码
#include<bits/stdc++.h>
#define INF 1000000
using namespace std;
int s=0,t,n,m,u,v;
int head[305],nex[20005],to[20005],cap[20005],tp=-1;
int fir[305],dep[305],go[305];
bool bj[305];
int dfs(int x,int now){
if(x==t||!now) return now;
int c=0;
for(int &i=fir[x];~i;i=nex[i]){
if(cap[i]&&dep[to[i]]==dep[x]+1){
int f=dfs(to[i],min(cap[i],now));
c+=f;
now-=f;
cap[i]-=f;
cap[i^1]+=f;
if(!now) break;
}
}
return c;
}
inline int bfs(){
memset(dep,0,sizeof(dep));
dep[s]=1;
queue<int>q;
q.push(s);
while(!q.empty()){
int x=q.front();
q.pop();
for(int i=head[x];~i;i=nex[i]){
if(cap[i] && !dep[to[i]]){
dep[to[i]]=dep[x]+1;
q.push(to[i]);
}
}
}
return dep[t];
}
inline int Dinic(){
int c=0;
while(bfs()){
for(int i=0;i<=2*n+1;++i) fir[i]=head[i];
c+=dfs(s,INF);
}
return c;
}
inline void add(int x,int y,int v){
nex[++tp]=head[x];
head[x]=tp;
to[tp]=y;
cap[tp]=v;
}
int main(){
memset(head,-1,sizeof(head));
scanf("%d%d",&n,&m);t=2*n+1;
for(int i=1;i<=n;++i) {
add(s,i,1);add(i,s,0);
}
for(int i=n+1;i<=2*n;++i){
add(i,t,1);add(t,i,0);
}
for(int i=1;i<=m;++i){
scanf("%d%d",&u,&v);
add(u,n+v,1);
add(n+v,u,0);
}
int ans=n-Dinic();
for(int i=1;i<=n;++i){
for(int j=head[i];~j;j=nex[j]){
int v=to[j];
if(!cap[j] && v!=s){
go[i]=v-n;
bj[v-n]=true;
}
}
}
for(int i=1;i<=n;++i){
if(!bj[i]){
int p=i;
while(p){
printf("%d ",p);
p=go[p];
}
printf("\n");
}
}
printf("%d\n",ans);
return 0;
}