【ACWing】1126. 最小花费

题目地址:

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 1n2000
m ≤ 1 0 5 m≤10^5 m105

假设 A A A一开始手里有 1 1 1块钱,将转账看成是在图上走过一条边,这条边的权是 1 − z % 1-z\% 1z%,并且每走过一条边,就乘上这个权值。那么相当于要问,从 A A A走到 B B B的最大权值乘积是多少。因为 1 − z % > 0 1-z\%>0 1z%>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)

猜你喜欢

转载自blog.csdn.net/qq_46105170/article/details/115112045
今日推荐