[C++一本通-图论算法] 例4-4 最小花费

题目描述###

在n个人中,某些人的银行账号之间可以互相转账。这些人之间转账的手续费各不相同。给定这些人之间转账时需要从转账金额里扣除百分之几的手续费,请问A最少需要多少钱使得转账后B收到100元。

输入输出格式###

输入格式:####

第一行输入两个正整数n,m,分别表示总人数和可以互相转账的人的对数。
以下m行每行输入三个正整数x,y,z,表示标号为x的人和标号为y的人之间互相转账需要扣除z%的手续费 (z<100)。
最后一行输入两个正整数A,B。数据保证A与B之间可以直接或间接地转账。

输出格式:####

输出A使得B到账100元最少需要的总费用。精确到小数点后8位。

输入输出样例###

输入样例#1:####

3 3
1 2 1
2 3 2
1 3 3
1 3

输出样例#1:####

103.07153164

数据范围说明###

1<=n<=2000

解题思路###

这一题数据较大,所以用Dijkstra,\(O(n^2)\) 实现。由于从一个账户转钱到另外一个账户的手续费不同,所以我们视它们为边的权值。先求出去掉手续费后所遗留下的钱,并在寻找路径时尽量去找能留下更多钱的路径(注意在起始点时要给它赋值为1),并将权值相乘,最后拿100除以权值相乘得到的最大值,输出答案。

实现代码####

#include <iostream>
#include <cstdio>
using namespace std;

const int M = 2002;
int n,m,a,b,k;
double map[M][M],minn;
double dis[M];
bool vis[M];

void dijkstra(int start)
{
    for(int i=1; i<=n; i++)
        dis[i]=map[start][i];
    vis[start]=true;
    dis[start]=1;//必须进行赋值1,因为若进行更改将使用乘法(0*every==0) 
    for(int i=1; i<n; i++)
    {
        minn=0;
        for(int j=1; j<=n; j++)
            if(!vis[j]&&dis[j]>minn)   //寻找剩余金额最大的 
                minn=dis[j],k=j;
        vis[k]=true;
        if(k==b) break;//结束标志
        for(int q=1; q<=n; q++)
            if(!vis[q]&&dis[q]<dis[k]*map[k][q])
                dis[q]=dis[k]*map[k][q];//进行松弛
    }
}

int main()
{
    double z;
    scanf("%d%d",&n,&m);
    for(int i=1,x,y; i<=m; i++)
    {
        cin>>x>>y>>z;
        map[x][y]=map[y][x]=(100-z)/100;//表示是原金额的百分之几
    }
    cin>>a>>b;
    dijkstra(a);
    printf("%.8lf\n",100/dis[b]);
    return 0;
}
posted @ 2018-10-03 15:34  _tham  阅读(995)  评论(0编辑  收藏  举报