[圖論]最小花費

最小花費


題目描述

在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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章