[图论]最小花费

最小花费


题目描述

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


输入格式

第一行输入两个用空格隔开的正整数n和m,分别表示总人数和可以互相转账的人的对数。以下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


解析

又是一道最短路的水题,只需注意题目给的百分比的运算,其他的就是套我们的模板。
本题解用的是Dijkstra算法,以下为Dijkstra算法的简单描述。
Dijkstra用来计算从一个点到其他所有点的最短路径的算法,是一种单源最短路径算法。也就是说,只能计算起点只有一个的情况。
Dijkstra的时间复杂度是O (N2),它不能处理存在负边权的情况。
算法描述:

   设起点为s,dis[v]表示从s到v的最短路径,pre[v]为v的前驱节点,用来输出路径。
   初始化:dis[v]=(v≠s); dis[s]=0; pre[s]=0; 
   For (i = 1; i <= n ; i++)
        1.在没有被访问过的点中找一个顶点u使得dis[u]是最小的。
        2.u标记为已确定最短路径
        3.For 与u相连的每个未确定最短路径的顶点v
          if  (dis[u]+w[u][v] < dis[v]) 
           {
              dis[v] = dis[u] + w[u][v];
              pre[v] = u;
           }
    算法结束:dis[v]为s到v的最短距离;pre[v]为v的前驱节点,用来输出路径。

Dijkstra算法扩展


代码

#include<cmath> 
#include<stdio.h>
#include<iostream>
using namespace std;
int n,m,u[2005],t1,t2;
double b[2005],a[2005][2005];
int main(){
	scanf("%d%d",&n,&m);
	for(int i=1;i<=m;i++){
		int xxx,ooo,ooxx;
		scanf("%d%d%d",&xxx,&ooo,&ooxx);
		a[xxx][ooo]=a[ooo][xxx]=(100-ooxx)/100.0; //计算百分比
	}
	scanf("%d%d",&t1,&t2);
	for(int i=1;i<=n;i++)
	 b[i]=a[t1][i];
	u[t1]=1; //标记初始点t1
	b[t1]=1; //自己到自己的代价为 100%
	for(int i=1;i<=n-1;i++){
		int o=0;
		for(int j=1;j<=n;j++){
			if(!u[j] and b[j]>b[o]) //与平常不同的,因为这里是比谁的百分比的
			 o=j; //找到最小点并标记
		}
		u[o]=1; //标记u[o]为白点
		for(int j=1;j<=n;j++){ 
			if(!u[j] and b[o]*a[o][j]>b[j]) //注意,这里是b[o]*a[o][j],因为计算的是百分比,所以用乘法。大于号就不解释了,同上
			 b[j]=b[o]*a[o][j]; //更新百分比
		}
	} 
	printf("%.8lf",100/b[t2]); //反过来用100来除以百分比
	return 0;
}
posted @ 2019-12-28 09:07  学校开除后  阅读(104)  评论(0编辑  收藏  举报