题目地址:
https://www.acwing.com/problem/content/1128/
在 n n n个人中,某些人的银行账号之间可以互相转账。这些人之间转账的手续费各不相同。给定这些人之间转账时需要从转账金额里扣除百分之几的手续费,请问 A A A最少需要多少钱使得转账后 B B B收到 100 100 100元。
输入格式:
第一行输入两个正整数 n , m n,m n,m,分别表示总人数和可以互相转账的人的对数。以下 m m m行每行输入三个正整数 x , y , z x,y,z x,y,z,表示标号为 x x x的人和标号为 y y y的人之间互相转账需要扣除 z z% z的手续费( z < 100 z<100 z<100)。最后一行输入两个正整数 A , B A,B A,B。数据保证 A A A与 B B B之间可以直接或间接地转账。
输出格式:
输出 A A A使得 B B B到账 100 100 100元最少需要的总费用。精确到小数点后 8 8 8位。
数据范围:
1 ≤ n ≤ 2000 1≤n≤2000 1≤n≤2000
m ≤ 1 0 5 m≤10^5 m≤105
假设 A A A一开始手里有 1 1 1块钱,将转账看成是在图上走过一条边,这条边的权是 1 − z % 1-z\% 1−z%,并且每走过一条边,就乘上这个权值。那么相当于要问,从 A A A走到 B B B的最大权值乘积是多少。因为 1 − z % > 0 1-z\%>0 1−z%>0,将每条边的权值取对数再取反,就将问题转为非负权无向图的最短路问题,可以用Dijkstra算法来做。对该图而言,可以用朴素版Dijkstra算法来做。参考https://blog.csdn.net/qq_46105170/article/details/113816110。代码如下:
#include <iostream>
#include <cstring>
using namespace std;
const int N = 2010;
int n, m, S, T;
double g[N][N];
double dist[N];
bool st[N];
void dijkstra() {
dist[S] = 1;
for (int i = 1; i <= n; i++) {
int t = -1;
for (int j = 1; j <= n; j++)
if (!st[j] && (t == -1 || dist[t] < dist[j]))
t = j;
// 搜到终点了就可以直接返回了
if (t == T) return;
st[t] = true;
for (int j = 1; j <= n; j++)
if (!st[j] && dist[j] < dist[t] * g[t][j])
dist[j] = dist[t] * g[t][j];
}
}
int main() {
cin >> n >> m;
for (int i = 0; i < m; i++) {
int a, b, c;
scanf("%d%d%d", &a, &b, &c);
double z = (100.0 - c) / 100;
g[a][b] = g[b][a] = max(g[a][b], z);
}
cin >> S >> T;
dijkstra();
printf("%.8lf\n", 100 / dist[T]);
return 0;
}
时间复杂度 O ( m log n ) O(m\log n) O(mlogn),空间 O ( n 2 ) O(n^2) O(n2)。