[网络流24题 #3]最小路径覆盖问题

【问题分析】
有向无环图最小路径覆盖,可以转化成二分图最大匹配问题,从而用最大流解决。
【建模方法】

构造二分图,把原图每个顶点i拆分成二分图X,Y集合中的两个顶点Xi和Yi。对于原图中存在的每条边(i,j),在二分图中连接边(Xi,Yj)。然后把二分图最大匹配模型转化为网络流模型,求网络最大流。

最小路径覆盖的条数,就是原图顶点数,减去二分图最大匹配数。沿着匹配边查找,就是一个路径上的点,输出所有路径即可。
【建模分析】
对于一个路径覆盖,有如下性质:

1、每个顶点属于且只属于一个路径。
2、路径上除终点外,从每个顶点出发只有一条边指向路径上的另一顶点。

所以我们可以把每个顶点理解成两个顶点,一个是出发,一个是目标,建立二分图模型。该二分图的任何一个匹配方案,都对应了一个路径覆盖方案。如果匹配数为0,那么显然路径数=顶点数。每增加一条匹配边,那么路径覆盖数就减少一个,所以路径数=顶点数 - 匹配数。要想使路径数最少,则应最大化匹配数,所以要求二分图的最大匹配。

注意,此建模方法求最小路径覆盖仅适用于有向无环图,如果有环或是无向图,那么有可能求出的一些环覆盖,而不是路径覆盖。


方案很多,这里不输出方案,仅是求出最大流:

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <vector>
#define push(x) push_back(x)
#define MaxN 120
using namespace std;
const int INF=~0U>>2;
int head[MaxN],h[MaxN],v[MaxN];
int n,m,tot,s,t;
struct edge
{
	int v,cap,next;
	edge(int x,int y,int c):v(y),cap(c),next(head[x])
	{
		head[x]=tot++;
	}
};
vector<edge> a;
inline void AddEdge(int x,int y,int c)
{
	a.push(edge(x,y,c));
	a.push(edge(y,x,0));
}
int sap(int x,int lim)
{
	if(x==t) return lim;
	int sum=0,y,flow;
	for(int i=head[x];i!=-1;i=a[i].next)
	{
		y=a[i].v;
		if(a[i].cap>0&&h[y]+1==h[x])
		{
			flow=sap(y,min(a[i].cap,lim-sum));
			a[i].cap-=flow;
			a[i^1].cap+=flow;
			sum+=flow;
			if(sum==lim) return sum;
		}
	}
	if(h[s]<=t||!sum)
	{
		if(!--v[h[x]]) h[s]=t+1;
		v[++h[x]]++;
	}
	return sum;
}
inline int mf()
{
	int ans=0;
	v[0]=t+1;
	while(h[s]<=t)
		ans+=sap(s,INF);
	return ans;
}
inline void init()
{
	int x,y;
	memset(head,-1,sizeof(head));
	cin>>n>>m;
	t=2*n+1; 
	while(m--)
		scanf("%d%d",&x,&y),
		AddEdge(x,y+n,1);
	for(int i=1;i<=n;i++)
		AddEdge(s,i,1),
		AddEdge(n+i,t,1);
}
inline void work()
{
	int ans=n-mf();
	cout<<ans<<endl;
}
int main()
{
	init();
	work();
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值