[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;
}