洛谷:P1730 最小密度路径(多条件状态维护 + Floyd变式 + 枚举维护最优子结构)

洛谷:最小密度路径

在这里插入图片描述
给定一个图,Q次询问两个节点之间的最小密度 (密度的定义为,两点之间路径上边的权值和 除以 边的数量

观察数据,类多源最短路,显然用 Floyd 求解

  • 但普通 Floyd 维护的是两点间的最短路,此题要维护的是自定义的密度

  • 密度尽可能小,相当于需要路径上边权和尽可能小、边数量尽可能多,同时维护两个变量是比较困难的

所以可以考虑先固定一个变量,再维护另一个变量的所有情况;枚举所有固定的情况,即所有最优子结构,就能求出全局最优解

所以我们选择枚举边数

  • 定义 dp 集合 f [ i ] [ j ] [ k ] f[i][j][k] f[i][j][k]表示从 i i i j j j 经过 k k k 条边的最短路

  • 考虑 dp 状态转移:显然我们有 f [ i ] [ j ] [ k ] = f [ i ] [ t ] [ a ] + f [ t ] [ j ] [ b ] f[i][j][k] = f[i][t][a]+f[t][j][b] f[i][j][k]=f[i][t][a]+f[t][j][b] (a + b == k)

  • 但这样 a、b都需要去枚举,变成五层循环了时间上没法接受;所以优化一下转移,每次用前一层维护出后一层

  • 于是得到了 f [ i ] [ j ] [ k ] = f [ i ] [ t ] [ k − 1 ] + f [ t ] [ j ] [ 1 ] f[i][j][k] = f[i][t][k-1]+f[t][j][1] f[i][j][k]=f[i][t][k1]+f[t][j][1] (0 < k < n)

Floyd 预处理后,询问时枚举边数(有向无环图,最短路显然不会超过n - 1条边), f [ i ] [ j ] [ k ] f[i][j][k] f[i][j][k] 中取最小的 ( f [ i ] [ j ] [ k ] / k ) (f[i][j][k]/k) (f[i][j][k]/k) (0<k<n) 即是答案

代码:

#include<bits/stdc++.h>
#include<unordered_set>
#include<unordered_map>
#define mem(a,b) memset(a,b,sizeof a)
#define cinios (ios::sync_with_stdio(false),cin.tie(0),cout.tie(0))
#define sca scanf
#define pri printf
#define ul (u << 1)
#define ur (u << 1 | 1)
#define fx first
#define fy second
//#pragma GCC optimize(2)
//[博客地址](https://blog.csdn.net/weixin_51797626?t=1) 
using namespace std;

typedef long long ll;
typedef pair<int, int> PII;
typedef pair <int, PII> PI;

const int N = 60, M = 1010, MM = N;
int INF = 0x3f3f3f3f, mod = 100003;
ll LNF = 0x3f3f3f3f3f3f3f3f;
int n, m, k, T, S, D;
int f[N][N][M];

void floyd() {
	for (int g = 2; g <= n; g++)//枚举边数(观察发现不需要枚举到 m)
		for (int k = 1; k <= n; k++)//
			for (int i = 1; i <= n; i++)
				for (int j = 1; j <= n; j++)
					f[i][j][g] = min(f[i][j][g], f[i][k][g - 1] + f[k][j][1]);
	//变式floyd,在枚举第三个转换点外再加了一层枚举边的状态转移
}

int main() {

	cin >> n >> m;
	for (int k = 1; k <= m; k++)
		for (int i = 1; i <= n; i++)
			for (int j = 1; j <= n; j++)
				f[i][j][k] = INF;//初始化极值

	for (int i = 1; i <= m; i++)
	{
		int a, b, x;
		sca("%d%d%d", &a, &b, &x);
		f[a][b][1] = min(f[a][b][1], x);//a -> b的一条有向边,注意重边
	}

	floyd();

	cin >> k;
	while (k--)
	{
		int a, b;
		sca("%d%d", &a, &b);

		double ans = 1e18;
		for (int i = 1; i <= n; i++)//每经过 i 条边的状态都在之前维护成最优解
			//枚举所有局部、子结构,就能得到全局最优解,是一种普遍求最优解的方法
			if (f[a][b][i] != INF)
				ans = min(ans, (double)f[a][b][i] / i);

		if (ans != 1e18)pri("%.3f\n", ans);
		else puts("OMG!");
	}

	return 0;
}

dpyyds

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值