【最小路径覆盖】最小路径覆盖

算法实现题8-3 最小路径覆盖问题(习题 8-13)
´问题描述:
给定有向图 G=(V,E)。设 P 是 G 的一个简单路(顶点不相交)的集合。如果 V 中每个
顶点恰好在 P 的一条路上,则称 P是 G 的一个路径覆盖。P 中路径可以从 V 的任何一个顶
点开始,长度也是任意的,特别地,可以为0。G 的最小路径覆盖是 G 的所含路径条数最少
的路径覆盖。
设计一个有效算法求一个有向无环图G 的最小路径覆盖。
提示:设V={1,2,º ,n},构造网络G1=(V1,E1)如下:
{ } { } n n y y y x x x , , , , , , V1 1 0 1 0   » = ,
{ } { } { } E j i y x V i y y V i x x j i i i
Œ » Œ » Œ = ) . ( : ) , ( : ) , ( : ) , ( E1 0 0  
每条边的容量均为1。求网络G1的( 0 x ,
0 y )最大流。
´编程任务:
对于给定的给定有向无环图G,编程找出 G的一个最小路径覆盖。
´数据输入:
由文件input.txt提供输入数据。文件第1 行有 2个正整数 n和 m。n是给定有向无环图
G 的顶点数, m是G 的边数。 接下来的m行, 每行有 2 个正整数 i和 j, 表示一条有向边(i,j)。  
´结果输出:
程序运行结束时,将最小路径覆盖输出到文件 output.txt 中。从第 1 行开始,每行输出


有两种解法:

1、网络流。不懂

2、二分图匹配。


很好理解。。把一个点拆成两个点,并分成二分图。原来(i,j)∈E0,则(i,j')属于E。

求一个最大匹配。如果左半集中一个点i与右半集j'中一个点匹配,认为j‘是i的后继(因为在这个问题中,一个点一定只有一个后继!!)

最后我们容易得出,如果i有后继,i'无前驱,则表明i是一条路径的起点。如果i有后继且i'有前驱,则i是路径中的点。如果i无后继,而i'有前驱则i是终点。

我们找到所有终点即路径数。等于总结点数减去最大匹配数。

#include <cstring>
#include <cstdio>

struct node
{
	long ind;
	node* nxt;	
};
node* head[100000];
bool vis[100000];
long lnk[100000];
long ans = 0;
long to[100000];

void insert(long a,long b)
{
	node* nn = new node;
	nn -> ind = b;
	nn -> nxt = head[a];
	head[a] = nn;	
}

bool Hungary(long u)
{
	for (node* vv=head[u];vv;vv=vv->nxt)
	{
		long v = vv -> ind;
		if (!vis[v])
		{
			vis[v] = true;
			if (!lnk[v]||Hungary(lnk[v]))
			{
				lnk[v] = u;
				return true;
			}		
		}	
	}
	return false;
}

int main()
{
	freopen("path.in","r",stdin);
	freopen("path.out","w",stdout);
	
	long n;long m;
	scanf("%ld%ld",&n,&m);
	for (long i=1;i<m+1;i++)
	{
		long i;long j;
		scanf("%ld%ld",&i,&j);
		insert(i,j);
	}   
    for (long i=1;i<n+1;i++)
    {
		memset(vis,0,sizeof vis);
		if (Hungary(i)) ans++;
	}
	for (long i=1;i<n+1;i++)
	{
		to[lnk[i]] = i;	
	}
	for (long i=1;i<n+1;i++)
	{
		if (to[i] && !lnk[i])
		{
			long ths = i;
			while (ths)
			{
				printf("%ld ",ths);
				ths = to[ths];
			}
			printf("\n");
		}
	}
	printf("%ld",n-ans);
	return 0;
}


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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值