看起来这么专有的名词应该是最大流的一个典型应用吧。。积累下来。。
这个要求的是最小的方案数。。然而最大流是要跑最大的丫。。所以肯定是把总的点数去减最大流了。。
于是很明白最大流其实就要表示那些不能表示一条路径的点。。一般来说这些点应该是路径中间的点。。那么剩下起点和终点要留下谁不跑流量呢?根据有向边来看。。应该就是留下终点了吧。。
那么建图方法就很明确了。。将流量跑向有向边指向的那个点。。由于是简单了路径,要限制最多只有一个点流经该点。。而且这个流量来源还不能是自己。。所以就考虑拆点。。然后自己和自己的另一个点不连。。那么这图就可以解释为,将所有流量都流进全部点中,看通过这些点能不能通过有向边流进其他点中,如果不能,这就是简单路径的终点了。。进而就可以求出最小覆盖路径的条数。。
然后方案显然可以找有流量的边去拼接一下。。。
/** * ┏┓ ┏┓ * ┏┛┗━━━━━━━┛┗━━━┓ * ┃ ┃ * ┃ ━ ┃ * ┃ > < ┃ * ┃ ┃ * ┃... ⌒ ... ┃ * ┃ ┃ * ┗━┓ ┏━┛ * ┃ ┃ Code is far away from bug with the animal protecting * ┃ ┃ 神兽保佑,代码无bug * ┃ ┃ * ┃ ┃ * ┃ ┃ * ┃ ┃ * ┃ ┗━━━┓ * ┃ ┣┓ * ┃ ┏┛ * ┗┓┓┏━┳┓┏┛ * ┃┫┫ ┃┫┫ * ┗┻┛ ┗┻┛ */ #include<cstdio> #include<cstring> #include<algorithm> #include<iostream> #include<queue> #include<cmath> #include<map> #include<stack> #include<set> #define inc(i,l,r) for(int i=l;i<=r;i++) #define dec(i,l,r) for(int i=l;i>=r;i--) #define link(x) for(edge *j=h[x];j;j=j->next) #define mem(a) memset(a,0,sizeof(a)) #define ll long long #define eps 1e-12 #define succ(x) (1<<x) #define lowbit(x) (x&(-x)) #define sqr(x) ((x)*(x)) #define mid (x+y>>1) #define NM 20005 #define nm 200005 #define pi 3.1415926535897931 using namespace std; const int inf=1000000005; ll read(){ ll x=0,f=1;char ch=getchar(); while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();} while(isdigit(ch))x=x*10+ch-'0',ch=getchar(); return f*x; } struct edge{int t,v;edge*next,*rev;}e[nm],*h[NM],*o=e,*tmp[NM],*p[NM]; void _add(int x,int y,int v){o->t=y;o->v=v;o->next=h[x];h[x]=o++;} void add(int x,int y,int v){_add(x,y,v);_add(y,x,0);h[x]->rev=h[y];h[y]->rev=h[x];} int n,m,_x,_y; int d[NM],cnt[NM],tot,next[NM],f[NM]; int maxflow(){ int flow=0;edge*j; inc(i,0,n)tmp[i]=h[i]; cnt[0]=tot=n+1; for(int x=0,s=inf;d[x]<tot;){ for(j=tmp[x];j;j=j->next)if(j->v&&d[x]==d[j->t]+1)break; if(j){ s=min(s,j->v);p[j->t]=tmp[x]=j; if((x=j->t)==n){ for(;p[x];x=p[x]->rev->t)p[x]->v-=s,p[x]->rev->v+=s; flow+=s;s=inf; } }else{ if(!--cnt[d[x]])break;d[x]=tot; link(x)if(j->v&&d[x]>d[j->t]+1)tmp[x]=j,d[x]=d[j->t]+1; cnt[d[x]]++; if(p[x])x=p[x]->rev->t; } } //printf("%d\n",flow); return flow; } int find(int x){return f[x]==x?x:f[x]=find(f[x]);} int main(){ n=read();m=read(); inc(i,1,n)add(0,i,1),add(i+n,2*n+1,1); while(m--){ _x=read();_y=read();add(_x,n+_y,1); } _x=n;n=n*2+1; int ans=maxflow();n=_x; inc(i,1,n)f[i]=i; inc(i,1,n)link(i)if(j->t>n&&!j->v)next[i]=j->t-n,f[find(j->t-n)]=find(i); inc(i,1,n)if(f[i]==i){ for(int j=i;j;j=next[j])printf("%d ",j); putchar('\n'); } return 0*printf("%d\n",n-ans); }
P2764 最小路径覆盖问题
题目描述
«问题描述:
给定有向图G=(V,E)。设P 是G 的一个简单路(顶点不相交)的集合。如果V 中每个顶点恰好在P 的一条路上,则称P是G 的一个路径覆盖。P 中路径可以从V 的任何一个顶点开始,长度也是任意的,特别地,可以为0。G 的最小路径覆盖是G 的所含路径条数最少的路径覆盖。设计一个有效算法求一个有向无环图G 的最小路径覆盖。提示:设V={1,2,.... ,n},构造网络G1=(V1,E1)如下:
每条边的容量均为1。求网络G1的( 0 x , 0 y )最大流。
«编程任务:
对于给定的给定有向无环图G,编程找出G的一个最小路径覆盖。
输入输出格式
输入格式:件第1 行有2个正整数n和m。n是给定有向无环图G 的顶点数,m是G 的边数。接下来的m行,每行有2 个正整数i和j,表示一条有向边(i,j)。
输出格式:从第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 4 7 10 11 2 5 8 3 6 9 3
说明
1<=n<=150,1<=m<=6000
由@zhouyonglong提供SPJ