Luogu 2764 最小路径覆盖问题Libre 6002 「网络流 24 题」最小路径覆盖 (网络流,最大流)...

mac2022-06-30  21

Luogu 2764 最小路径覆盖问题 / Libre 6002 「网络流 24 题」最小路径覆盖 (网络流,最大流)

Description

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

Input

第1行有2个正整数n和m。n是给定有向无环图G的顶点数,m是G的边数。 接下来的m行,每行有2个正整数i 和j,表示一条有向边(i,j)。

Output

第1行开始,每行输出一条(字典序)路径。最后一行是最少路径数。

Sample Input

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

Sample Output

1 4 7 10 11 2 5 8 3 6 9 3

Http

Luogu:https://www.luogu.org/problem/show?pid=2764 Libre:https://loj.ac/problem/6002

Source

网络流,最大流

解决思路

这题是sdoi2010星际竞速的弱化版,去掉了费用的限制。 对于每一个点u,我们把它拆成两个点u和u+n,分别作为入点和出点。在源点与所有入点之间连一条容量为1的边,在所有的出点与汇点之间也连一条容量为1的边。再对于每一条有向边u->v,我们连接u的入点和v的出点,容量为1. 这样建图的目的是保证任何一个点只进去一次,出来一次,并且每个点至少都要走一次。 至于如何找出路径呢? 假设我们现在选择第u个点出发,则寻找与其相连的所有边中满足相连的点v为出点且残量为0,因为我们保证了一个点只走一次,所以这个点一定是唯一的。然后再以v为出发点寻找,直到找不到为止。需要注意的是,为了防止重复计算,上面我们经过的所有点都要标记一下,后面就不再经过了。 另:这里使用Dinic实现最大流,关于Dinic算法,请移步我的这篇文章

代码

#include<iostream> #include<cstdio> #include<cstdlib> #include<cstring> #include<algorithm> using namespace std; const int maxN=500; const int maxM=60000; const int inf=2147483647; class Edge { public: int v,flow; }; int n,m; int cnt=-1; int Head[maxN]; int Next[maxM]; Edge E[maxM]; int depth[maxN]; int Q[maxM]; int cur[maxN]; bool vis[maxN]; void Add_Edge(int u,int v,int flow); bool bfs(); int dfs(int u,int flow); int main() { memset(Head,-1,sizeof(Head)); scanf("%d%d",&n,&m); for (int i=1;i<=n;i++) { Add_Edge(0,i,1);//连接源点与入点 Add_Edge(i+n,2*n+1,1);//连接出点与汇点 } for (int i=1;i<=m;i++) { int u,v; scanf("%d%d",&u,&v); Add_Edge(u,v+n,1);//连接u的入点与v的出点 } while (bfs()) { for (int i=0;i<=n*2+1;i++)//当前弧优化 cur[i]=Head[i]; while (int di=dfs(0,inf)); } memset(vis,0,sizeof(vis)); int Ans=0; for (int i=1;i<=n;i++)//统计路径 { if (vis[i]==1) continue; int now=i; bool get; do { cout<<now<<" "; vis[now]=1; get=0; for (int i=Head[now];i!=-1;i=Next[i]) if ((E[i].flow==0)&&(E[i].v>n)&&(E[i].v<=2*n)) { now=E[i].v-n; get=1; break; } } while (get==1); cout<<endl; Ans++; } cout<<Ans<<endl;//输出路径条数 return 0; } void Add_Edge(int u,int v,int flow)//加边 { cnt++; Next[cnt]=Head[u]; Head[u]=cnt; E[cnt].v=v; E[cnt].flow=flow; cnt++; Next[cnt]=Head[v]; Head[v]=cnt; E[cnt].v=u; E[cnt].flow=0; } bool bfs()//bfs求层次图 { memset(depth,-1,sizeof(depth)); int t=0,h=1; Q[1]=0; depth[0]=1; do { t++; int u=Q[t]; for (int i=Head[u];i!=-1;i=Next[i]) { int v=E[i].v; if ((E[i].flow>0)&&(depth[v]==-1)) { depth[v]=depth[u]+1; h++; Q[h]=v; } } } while (h!=t); if (depth[n*2+1]==-1)//当汇点不存在层次图中时,说明增广完毕 return 0; return 1; } int dfs(int u,int flow)//dfs增广 { if (u==n*2+1) return flow; for (int &i=cur[u];i!=-1;i=Next[i]) { int v=E[i].v; if ((depth[v]==depth[u]+1)&&(E[i].flow>0)) { int di=dfs(v,min(flow,E[i].flow)); if (di>0) { E[i].flow-=di; E[i^1].flow+=di; return di; } } } return 0; }

转载于:https://www.cnblogs.com/SYCstudio/p/7267621.html

相关资源:JAVA上百实例源码以及开源项目
最新回复(0)