## [$AcWing$ $1126$. 最小花费](https://www.acwing.com/problem/content/1128/) ### 一、题目描述 在 $n$ 个人中,某些人的银行账号之间可以互相转账。 这些人之间转账的手续费各不相同。 给定这些人之间转账时需要从转账金额里扣除百分之几的手续费,请问 $A$ 最少需要多少钱使得转账后 $B$ 收到 $100$ 元。 **输入格式** 第一行输入两个正整数 $n,m$,分别表示总人数和可以互相转账的人的对数。 以下 $m$ 行每行输入三个正整数 $x,y,z$,表示标号为 $x$ 的人和标号为 $y$ 的人之间互相转账需要扣除 $z\%$ 的手续费 ( $z<100$ )。 最后一行输入两个正整数 $A,B$。 数据保证 $A$ 与 $B$ 之间可以直接或间接地转账。 **输出格式** 输出 $A$ 使得 $B$ 到账 $100$ 元最少需要的总费用。 精确到小数点后 $8$ 位。 **数据范围** $1≤n≤2000,m≤10^5$ **输入样例**: ```cpp {.line-numbers} 3 3 1 2 1 2 3 2 1 3 3 1 3 ``` **输出样例**: ```cpp {.line-numbers} 103.07153164 ``` ### 二、题目解析 假设初始金钱为$N$,那么如果要在最后一个人的手里得到$100$元,可得公式: $$\large N∗(1−z_1\%)∗(1−z_2\%)∗…∗(1−z_n\%)=100$$ $\Rightarrow$ $$\large N=\frac{100}{(1−z_1\%)∗(1−z_2\%)∗…∗(1−z_n\%)}$$ 要想$N$尽可能小,那么就要让 **分母尽可能大** ,即求$(1−z_1\%)∗(1−z_2\%)∗…∗(1−z_n\%)$的最大值。 **注意**: | | 最小值 | 最大值 | | ------------ | ---------------------------------------------------- | ------------------------ | | **优先队列** | **小根堆** | **大根堆** | | **实现** | ` priority_queue, greater> q;` | `priority_queue q;` | 更新的时候用:$d[v] = d[u] * (1 - w[i]\%)$。 ### 三、$Dijkstra$ ```cpp {.line-numbers} #include using namespace std; const int N = 2010; const int M = 2e5 + 10; typedef pair PDI; // 堆中数值是浮点数,注意区别 int n, m; double dis[N]; bool st[N]; int h[N], e[M], ne[M], idx; double w[M]; // 边权为浮点数,与一般的题目有区别 void add(int a, int b, double c) { e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx++; } int S, T; void dijkstra() { priority_queue q; // 大顶堆 dis[S] = 1; q.push({1, S}); while (q.size()) { auto t = q.top(); q.pop(); int u = t.second; if (st[u]) continue; st[u] = true; for (int i = h[u]; ~i; i = ne[i]) { int v = e[i]; double a = 1 - w[i]; if (dis[v] < dis[u] * a) { dis[v] = dis[u] * a; q.push({dis[v], v}); } } } } int main() { memset(h, -1, sizeof h); cin >> n >> m; while (m--) { int a, b, c; cin >> a >> b >> c; double w = c * 0.01; add(a, b, w), add(b, a, w); } cin >> S >> T; dijkstra(); printf("%.8lf\n", 100 / dis[T]); return 0; } ```