题目描述:
题解思路:
假设初始转账人为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 }