P1576 最小花费

题目描述:

  传送门

题解思路:

  假设初始转账人为A,最终接账人为B。看起来感觉直接求A转账的钱并不好求,但是可以通过已经B收到的金额,再返回去推A转账的金额,一种逆序的思想,每次推算下一个点的时候都要除以(1-z%)往回推转账的钱,所有都逆过来了,因此很明显这道题是一个裸的最短路径问题。此题用dijkstra算法即可求解,唯一需要注意的是,更新点的权值不再是加法而变成了此题的乘法。

  注意:题目所给的边的上限不是很可靠,导致最后又2个点RE,然后修改边结构体的大小为原来的10倍才通过。

代码:

 1 #include<iostream>
 2 #include<string.h>
 3 #include<algorithm> 
 4 #include<queue> 
 5 #include<cstdio>
 6 using namespace std;
 7 int INFTY=1<<30;                //一个较大的值 
 8 #define re register
 9 
10 typedef pair<double,int> P;                //前面存到起始点的值,后面存点编号 
11 struct edge{
12     int next;        //下一条的编号 
13     double w;            //权值 
14     int to;            //当前边的终止点编号 
15 };
16 edge e[1000005];
17 int head[2005];
18 int cnt=0;
19 int n,m; 
20 int WHITE=0;
21 int BLACK=1;
22 int GRAY=2;
23 
24 void dij(int start,int end){    //start实际上是收账人,end是转账人 
25     priority_queue<P,vector<P>,greater<P> > que;        //最小值优先
26     double d[2005];                //存个点到起点的值
27     int color[2005];
28     
29     for(re int i=1;i<=n;i++){
30         color[i]=WHITE;        //表示未访问过
31         d[i]=INFTY;            //表示没有连接 
32     } 
33     que.push(P(100,start));                //起点入队
34     color[start]=GRAY;                        //记录访问过
35     d[start]=100;                        //100起步 
36     
37     while(!que.empty()){
38         P cur=que.top();
39         que.pop();
40         int u=cur.second;                //拿出点的编号
41         
42         if(color[u]==BLACK) continue;  
43         color[u]=BLACK;
44         
45         for(re int k=head[u];~k;k=e[k].next){        //遍历和temp有关的所有边 
46             if(color[e[k].to]==BLACK) continue;            //表示没访问过 
47             
48             int v=e[k].to;            //边的终点 
49             double money=d[u]/(1-(e[k].w)/100);
50             if(money<d[v]){                //注意要进行点的权值比较,若点权值能变小才会更新 
51                 que.push(P(money,v));
52                 d[v]=money;
53                 color[v]=GRAY;
54             }
55         }
56     }
57     
58     printf("%0.8lf",d[end]);            //输出最终某个人需要转账时的最小花费 
59 }
60 void add(int a,int b,double c){    //前向星存图 
61     e[cnt].to=b;
62     e[cnt].w=c;
63     e[cnt].next=head[a];
64     head[a]=cnt;
65     cnt++;
66 }
67 int main(){
68     memset(head,-1,sizeof(head));
69     cin>>n>>m;
70     
71     for(re int i=1;i<=m;i++){
72         int a,b;
73         double c;
74         cin>>a>>b>>c;
75         add(a,b,c);                    //无向图存图 
76         add(b,a,c);
77     }
78     
79     int a,b;
80     cin>>a>>b;
81     dij(a,b);
82     return 0;
83 }

 

posted @ 2020-05-04 17:39  neverstopcoding  阅读(217)  评论(0编辑  收藏  举报