1111-最小花费(中南大学19年研究生招生夏令营)

这篇博客介绍了如何使用Dijkstra算法求解在转账网络中的最长路径问题,以便找出使B收到100元时A所需支付的最小费用。通过修改Dijkstra算法的条件,从求最小生成树转变为寻找最长路径。代码示例展示了两种实现方式,一种是普通版本,另一种是堆优化版本,均能正确计算出所需费用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目描述
在n个人中,某些人的银行账号之间可以互相转账。这些人之间转账的手续费各不相同。给定这些人之间转账时需要从转账金额里扣除百分之几的手续费,请问A最少需要多少钱使得转账后B收到100元。
输入
输入包含多组测试用例。
对于每组样例,第一行输入两个正整数n,m,分别表示总人数和可以互相转账的人的对数。(0<n<=2000)以下m行每行输入三个正整数x,y,z。表示标号为x的人和标号为y的人之间互相转账需要扣除z%的手续费(z<100)。
最后一行输入两个正整数A,B。数据保证A与B之间可以直接或间接地转账
输出
输出A使得B到账100元最少需要的总费用。精确到小数点后8位。
样例输入
3 3
1 2 1
2 3 2
1 3 3
1 3
样例输出
103.07153164

题解代码如下

#include <bits/stdc++.h>
using namespace std;
struct edge{
    int to;
    double cost;            ///保留率
};
const int N = 2010;
vector<edge> v[N];
int vis[N];
double dist[N];
int n,m,s,t;

void dij(){
    memset(vis,0,sizeof vis);
    memset(dist,0,sizeof dist);

    for(int i=0;i<v[s].size();i++) dist[v[s][i].to] = v[s][i].cost;         ///首先将距离初始化
    vis[s] = 1;

    for(int i=1;i<n;i++){                                                   ///n-1个顶点只需要循环n-1次
        double maxv = -99999999;									///这里条件改了一下,会发现求的是最大生成树
        int maxIndex = -1;
        for(int j=1;j<=n;j++){
            if(!vis[j]&&maxv<dist[j]) maxv = dist[j],maxIndex = j;
        }
        if(maxIndex==-1) break;
        vis[maxIndex]=1;
        for(int j=0;j<v[maxIndex].size();j++){          ///用找到的这个下标去更新 新的顶点
            if(!vis[v[maxIndex][j].to]&&dist[v[maxIndex][j].to]<dist[maxIndex]*v[maxIndex][j].cost){ ///如果这个点连接的下一个点没有被访问,且到下一个点的原始距离大于经过这个点所到达的距离
                dist[v[maxIndex][j].to]=dist[maxIndex]*v[maxIndex][j].cost;                          ///更新这个点
            }
        }
    }
    if(dist[t]==0) puts("Impossible");
    else printf("%.8f\n",100/dist[t]);
}

int main(){
    while(~scanf("%d%d",&n,&m)){
        for(int i=1;i<=m;i++){              ///读入m条边,读边牵涉到顶点,这是从顶点1开始读入的
            int x,y,z;
            scanf("%d%d%d",&x,&y,&z);
            v[x].push_back({y,1.0-1.0*z/100});
            v[y].push_back({x,1.0-1.0*z/100});
        }

        scanf("%d%d",&s,&t);
        dij();

        for(int i=1;i<=n;i++) v[i].clear(); ///将顶点清空
    }

    return 0;
}

dijkstra算法可以求最小生成树,但这份代码改了一下条件,求的是最长路径,这也是dijkstra的板子题,但是double数据的处理以及循环的边界条件要十分注意

堆优化版本如下

#include<bits/stdc++.h>
using namespace std;
const int N = 2020;
int n,m,s,t;

struct edge{
    int to;
    double w;       ///表示起点到v的边权
};
struct node{
    int v;
    double d;                   ///d表示起点到d点的距离
    bool operator<(const node&rs)const{
        return d<rs.d;
    }
};
double dist[N];
vector<edge> mp[N];
void dij(){
    memset(dist,0x3f,sizeof dist);
    dist[s]=1;
    priority_queue<node> q;
    q.push({s,1});

    while(q.size()){
        auto now = q.top();q.pop();
        if(now.d<dist[now.v]) continue;

        for(int i=0;i<mp[now.v].size();i++){
            auto nx = mp[now.v][i];

            if(dist[nx.to]<dist[now.v]*nx.w){
                dist[nx.to]=dist[now.v]*nx.w;
                q.push({nx.to,dist[nx.to]});
            }
        }
    }
    printf("%.8f\n",100/dist[t]);
}
int main(){
    while(~scanf("%d%d",&n,&m)){
        for(int i=1;i<=m;i++){
            int x,y,z;
            scanf("%d%d%d",&x,&y,&z);
            mp[x].push_back({y,1.0-1.0*z/100});
            mp[y].push_back({x,1.0-1.0*z/100});
        }
        scanf("%d%d",&s,&t);
        dij();

        for(int i=1;i<=n;i++) mp[i].clear();
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值