luogu2764(最小路径覆盖->最大流)

看起来这么专有的名词应该是最大流的一个典型应用吧。。积累下来。。

这个要求的是最小的方案数。。然而最大流是要跑最大的丫。。所以肯定是把总的点数去减最大流了。。

于是很明白最大流其实就要表示那些不能表示一条路径的点。。一般来说这些点应该是路径中间的点。。那么剩下起点和终点要留下谁不跑流量呢?根据有向边来看。。应该就是留下终点了吧。。

那么建图方法就很明确了。。将流量跑向有向边指向的那个点。。由于是简单了路径,要限制最多只有一个点流经该点。。而且这个流量来源还不能是自己。。所以就考虑拆点。。然后自己和自己的另一个点不连。。那么这图就可以解释为,将所有流量都流进全部点中,看通过这些点能不能通过有向边流进其他点中,如果不能,这就是简单路径的终点了。。进而就可以求出最小覆盖路径的条数。。

然后方案显然可以找有流量的边去拼接一下。。。





/**
 *        ┏┓    ┏┓
 *        ┏┛┗━━━━━━━┛┗━━━┓
 *        ┃       ┃  
 *        ┃   ━    ┃
 *        ┃ >   < ┃
 *        ┃       ┃
 *        ┃... ⌒ ...  ┃
 *        ┃       ┃
 *        ┗━┓   ┏━┛
 *          ┃   ┃ 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);
}


扫描二维码关注公众号,回复: 694889 查看本文章





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 行开始,每行输出一条路径。文件的最后一行是最少路径数。

输入输出样例

输入样例#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

由@zhouyonglong提供SPJ


猜你喜欢

转载自blog.csdn.net/qkoqhh/article/details/80245897