Acwing 1126.最小花费【单源最短路】【Dijkstra】

1.题目
题目链接 点这儿!
2.解决方法
题目可以抽象为一张无向网,给定的是手续费百分比,边权值可以据此改为扣掉手续费后所剩下的钱的百分比(eg:a与b之间手续费比例为3%,则金钱缩水为0.97)这张无向网,从源点到终点,根据题目要求是要找一条边权乘积最大的路径(具体解释在代码注释部分)乘积最大问题,可以转化为最短路径问题。
3.代码

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;

const int MAXN=2010;
double g[MAXN][MAXN],dist[MAXN];
int n,m,A,B;
bool st[MAXN];

void dijkstra()//朴素dijkstra O(n^2)
{
    
    
    dist[A]=1;
    for(int i=1;i<=n;i++){
    
    
        int t=-1;
        for(int j=1;j<=n;j++)
            if(!st[j]&&(t==-1||dist[t]<dist[j]))
                t=j;
                
        st[t]=true;
        
        for(int j=1;j<=n;j++)
            dist[j]=max(dist[j],dist[t]*g[t][j]);
    }
}

int main(void)
{
    
    
    cin>>n>>m;
    
    //乘积最大问题,找到一条源点到终点权值乘积最大的路径
    //最短路问题变形,dist的含义是从源点到每个点的最大权值乘积
    //dist[j]=dist[j]+w[i]能否直接变成dist[j]=dist[t]*w[i]?
    //是可以的
    //dist[T]=dist[S]*w1*w2*...*wn可以通过两边同时取对数得到log(dist[T])=log(dist[S])+log(w1)+log(w2)+...+log(wn)
    //两边同时取反,也就是对数和部分要达到最小值,左边的-log(dist[T])才能达到最大
    //只有当0<=w[i]<=1时,log(w[i])<=0,取反后不会出现负值(即不会出现负权边),才能使用dijkstra算法,否则只能用spfa算法
    for(int i=0;i<m;i++){
    
    
        int a,b,c;
        cin>>a>>b>>c;
        double d=(100.0-c)/100;
        g[a][b]=g[b][a]=max(g[a][b],d);
    }
    
    cin>>A>>B;
    
    dijkstra();
    
    printf("%.8lf\n",100.0/dist[B]);
    
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_43579980/article/details/109538936