题目描述
一个有向无回路的图G=(V,E)的一个路径覆盖是一个其结点不相交的路径集合P,图中的每一个结点仅包含于P中的一条路径。路径可从任意结点开始和结束,且长度也为任意值,包括0。请写出一个有效算法,找出一个包含尽可能少的路径的路径覆盖图中的所有点。
例如下图至少用两条路径覆盖,路径可以是:1-5-3,2-4。
输入
输入文件第一行为n(n≤4000),表示图G的顶点个数,从第二行开始每行有两个数u,v表示存在边(u,v)。
输出
输出文件的第一行为所求的最少的路径覆盖数k。第二行至第k+1行为每条路径上以0结尾的顶点序列。
样例输入
5 1 2 1 5 2 3 2 4 5 3
样例输出
2 1 5 3 0 2 4 0
这题可以用匈牙利做,但我介绍一种用FF/dinic做的做法
把每个点拆成2个点,把i到j的连边视为i到j+n,建一个超级源点和超级汇点。跑FF/dinic
路径:找到每条有流量的边,例如(i,j+n),让to【i】等于j,这时,j一定不是开始的点,标号j。
找到每个未标号的点,顺着to找下来
另外,重边真的很烦,因为FF对于重边只会计算第一条读入的边,所以我下面程序取了个min
#include<iostream> #include<cstdio> #include<vector> #include<cstring> using namespace std; struct edge{ int to,rl,ll,fxb; }; int n;const int INF=1000000000; int x=1; vector<edge> g[8101]; bool used[8101];int ans=0; int a[100001],b[100001]; int _used[100001],_to[100001],h[4001][4001]; int dfs(int u,int t,int f) { if(u==t)return f; used[u]=1; for(int i=0;i<g[u].size();i++) { edge &e=g[u][i]; if(!used[e.to]&&e.rl>e.ll) { f=min(f,e.rl-e.ll); int d=dfs(e.to,t,f); if(d>0) { e.ll+=d; g[e.to][e.fxb].ll-=d; return d; } } } return 0; } int flow(int s,int t) { int ff=0; for(;;) { memset(used,0,sizeof(used)); int dd=dfs(s,t,INF); if(dd==0)return ff; ff+=dd; } } void doing(int x) { if(x==0)return; printf("%d ",x); doing(_to[x]); } int Find() { // for(int i=1;i<x;i++)cout<<a[i]<<" "<<b[i]<<" "<<h[a[i]][b[i]]<<endl; for(int i=1;i<x;i++) { if(g[a[i]][h[a[i]][b[i]]].ll==1 &&!_to[a[i]]) { _to[a[i]]=b[i]; _used[b[i]]=1; } } // for(int i=1;i<=n;i++)cout<<_to[i]<<" "<<_used[i]<<endl; for(int i=1;i<=n;i++) { if(!_used[i]) { doing(i);printf("0 "); } } } int main() { memset(h,127,sizeof(h)); scanf("%d",&n); while(scanf("%d%d",&a[x],&b[x])!=EOF) { int c=g[a[x]].size(); h[a[x]][b[x]]=min(h[a[x]][b[x]],c);//cout<<h[a[x]][b[x]]; g[a[x]].push_back(edge{b[x]+n,1,0,g[b[x]+n].size()}); g[b[x]+n].push_back(edge{a[x],0,0,g[a[x]].size()-1}); x++; } for(int i=1;i<=n;i++) { g[2*n+1].push_back(edge{i,1,0,g[i].size()}); g[i].push_back(edge{2*n+1,0,0,g[2*n+1].size()-1}); g[n+i].push_back(edge{2*n+2,1,0,g[2*n+2].size()}); g[2*n+2].push_back(edge{n+i,0,0,g[n+i].size()-1}); } cout<<n-flow(2*n+1,2*n+2)<<endl; Find(); return 0; }