Description
定义一条路径的密度为:路径上边的权值和除以边的数量.
给定一张
n
n
n 点
m
m
m 边的有向图,
q
q
q 次询问,每次询问给定
s
,
t
s,t
s,t,求
s
→
t
s \to t
s→t 的路径中密度最小的一条。
Analysis
n
n
n 最大只有
50
50
50,我们可以预处理。
设
d
p
i
,
j
,
l
dp_{i,j,l}
dpi,j,l 表示恰好经过
l
l
l 条边的情况下,
i
→
j
i \to j
i→j 的最短路。
状态转移方程显然为
d
p
i
,
j
,
l
=
min
k
∈
[
1
,
n
]
,
d
∈
[
0
,
l
]
{
d
p
i
,
k
,
d
+
d
p
k
,
j
,
l
−
d
}
dp_{i,j,l}=\min_{k \in [1,n],d\in[0,l]} \{dp_{i,k,d}+dp_{k,j,l-d} \}
dpi,j,l=mink∈[1,n],d∈[0,l]{dpi,k,d+dpk,j,l−d}
但实现这个转移需要五重循环,会 T 飞,如何减少时间呢?
可以发现,
d
p
i
,
j
,
l
dp_{i,j,l}
dpi,j,l 的选择路径
v
1
→
v
2
→
⋯
→
v
l
v_1 \to v_2 \to \cdots \to v_l
v1→v2→⋯→vl,只需要找到其中一个点进行决策即可,这里我们选择
v
l
v_l
vl,可得:
d
p
i
,
j
,
l
=
min
k
∈
[
1
,
n
]
{
d
p
i
,
k
,
l
−
1
+
d
p
k
,
j
,
1
}
dp_{i,j,l}=\min_{k \in [1,n]} \{dp_{i,k,l-1}+dp_{k,j,1} \}
dpi,j,l=mink∈[1,n]{dpi,k,l−1+dpk,j,1}
边界条件显然为 d p i , j , 1 = w ( i , j ) {dp_{i,j,1}}=w(i,j) dpi,j,1=w(i,j)
处理每个查询时,将路径长度 l e n len len 从 s → t s \to t s→t 枚举一遍,找出 d p s , t , l e n l e n \frac{dp_{s,t,len}}{len} lendps,t,len 的最小值即可。
Code
注:代码中的下标全部从 0 0 0 开始
// Problem: P1730 最小密度路径
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P1730
// Memory Limit: 125 MB
// Time Limit: 1000 ms
//
// Powered by CP Editor (https://cpeditor.org)
#include <bits/stdc++.h>
using namespace std;
using i64 = long long;
using ui64 = unsigned long long;
using i128 = __int128;
using ui128 = unsigned __int128;
using f4 = float;
using f8 = double;
using f16 = long double;
const int INF = 1e9;
template<class T>
bool chmax(T &a, const T &b){
if(a < b){ a = b; return true; }
return false;
}
template<class T>
bool chmin(T &a, const T &b){
if(a > b){ a = b; return true; }
return false;
}
signed main() {
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
int n, m;
cin >> n >> m;
vector dp(n, vector(n, vector<int>(m, INF)));
for (int i = 0, u, v, w; i < m; i++) {
cin >> u >> v >> w;
u--, v--;
chmin(dp[u][v][0], w);
}
for (int l = 1; l < m; l++)
for (int k = 0; k < n; k++)
for (int i = 0; i < n; i++)
for (int j = 0; j < n; j++)
chmin(dp[i][j][l], dp[i][k][l - 1] + dp[k][j][0]);
int q;
cin >> q;
for (int i = 0, u, v; i < q; i++){
cin >> u >> v;
u--, v--;
f8 ans = INF;
for(int l = 0; l < n; l++)
if(dp[u][v][l] < INF) chmin(ans, 1.0 * dp[u][v][l] / (l + 1));
if(ans == INF) printf("OMG!\n");
else printf("%.3lf\n", ans);
}
return 0;
}