最小花費
題目描述
在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的前驅節點,用來輸出路徑。
代碼
#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;
}