洛谷 P2764 最小路径覆盖问题(最大流)

本文介绍了一种解决有向无环图(GAP)最小路径覆盖问题的有效算法,通过构建网络并求解最大流来找到覆盖图中所有顶点的最短路径集合。文章提供了详细的算法步骤及C++实现代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目描述

给定有向图 G=(V,E)G=(V,E) 。设 PP 是 GG 的一个简单路(顶点不相交)的集合。如果 VV 中每个定点恰好在PP的一条路上,则称 PP 是 GG 的一个路径覆盖。PP中路径可以从 VV 的任何一个定点开始,长度也是任意的,特别地,可以为 00 。GG 的最小路径覆盖是 GG 所含路径条数最少的路径覆盖。设计一个有效算法求一个 GAP (有向无环图) GG 的最小路径覆盖。

提示:设 V=\{1,2,...,n\}V={1,2,...,n} ,构造网络 G_1=\{V_1,E_1\}G1​={V1​,E1​} 如下:

V_1=\{x_0,x_1,...,x_n\}\cup\{y_0,y_1,...,y_n\}V1​={x0​,x1​,...,xn​}∪{y0​,y1​,...,yn​}

E_1=\{(x_0,x_i):i\in V\}\cup\{(y_i,y_0):i\in V\}\cup\{(x_i,y_j):(i,j)\in E\}E1​={(x0​,xi​):i∈V}∪{(yi​,y0​):i∈V}∪{(xi​,yj​):(i,j)∈E}

每条边的容量均为 11 ,求网络 G_1G1​ 的 (x_0,y_0)(x0​,y0​) 最大流。

输入输出格式

输入格式:

 

第一行有 22 个正整数 nn 和 mm 。 nn 是给定\text{GAP}GAP(有向无环图) GG 的顶点数, mm 是 GG 的边数。接下来的 mm 行,每行有两个正整数 ii 和 jj 表示一条有向边 (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\leq n\leq 150,1\leq m\leq 60001≤n≤150,1≤m≤6000

由@FlierKing提供SPJ

解题思路

依旧是拆点转化成二分图然后dinic(),然后dfs输出路径。

代码如下

#include <iostream>
#include <vector>
#include <cstring>
#include <cmath>
#include <queue>
#define INF 0x3f3f3f3f
#define s 0
#define t 310
using namespace std;
vector<int> g[355];
struct Line{
	int r, w;
	bool dir;
	Line(int r, int w, bool dir): r(r), w(w), dir(dir){	}
};
vector<Line> line;
void add_line(int x, int y)
{
	line.push_back(Line(y, 1, 1));
	g[x].push_back(line.size() - 1);
	line.push_back(Line(x, 0, 0));
	g[y].push_back(line.size() - 1);
}
int deep[355];
bool bfs()
{
	queue<int> que;
	memset(deep, 0, sizeof(deep));
	que.push(s);
	deep[s] = 1;
	while(!que.empty()){
		int top = que.front();
		que.pop();
		for(int i = 0; i < g[top].size(); i ++){
			int z = g[top][i];
			if(!deep[line[z].r] && line[z].w){
				deep[line[z].r] = deep[top] + 1;
				que.push(line[z].r);
				if(deep[t])
					return true;
			}
		}
	}
	return  false;
}
int dfs(int x, int mix)
{
	if(!mix || x == t)
		return mix;
	int ap = 0;
	for(int i = 0; i < g[x].size(); i ++){
		int z = g[x][i];
		if(deep[x] < deep[line[z].r] && line[z].w){
			int p = dfs(line[z].r, min(mix, line[z].w));
			mix -= p;
			ap += p;
			line[z].w -= p;
			line[z^1].w += p;
			if(!mix)
				return ap;
		}
	}
	return ap;
}
int dinic()
{
	int ans = 0;
	while(bfs())
		ans += dfs(s, INF);
	return ans;
}
bool vis[155];
void dfs2(int x)
{
	for(int i = 0; i < g[x].size(); i ++){
		int z = g[x][i];
		if(!vis[line[z].r / 2] && line[z].dir && !line[z].w){
			vis[line[z].r / 2] = true;
			cout << " " << line[z].r / 2;
			dfs2(line[z].r - 1);
		}
	}
}
int main()
{
	int n, m;
	while(cin >> n >> m){
		for(int i = 1; i <= n; i ++){
			add_line(s, 2*i);
			add_line(2*i+1, t);
		}
		for(int i = 0; i < m; i ++){
			int x, y;
			cin >> x >> y;
			add_line(2*x, 2*y+1);
		}
		int sum = dinic();
		memset(vis, 0, sizeof(vis));
		for(int i = 1; i <= n; i ++){
			if(!vis[i]){
				vis[i] = true;
				cout << i;
				dfs2(2 * i);
				cout << endl;
			}
		}
		cout << n - sum << endl;
		for(int i = 0; i <= t; i ++)
			g[i].clear();
		line.clear();
	}
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值