题意
给定一个无向图,边权为 w i ( 0 ≤ w i ≤ 1 ) w_i(0\leq w_i\leq1) wi(0≤wi≤1)。定义两点之间的距离为路径上所有权值的乘积。求两点之间的最长路。
思路
假设距离为 w 1 w 2 … w k w_1w_2\dots w_k w1w2…wk,可以对其取对数处理,即 log w 1 w 2 … w k = log w 1 + log w 2 + … log w k \log w_1w_2\dots w_k = \log w_1 + \log w_2 + \dots \log w_k logw1w2…wk=logw1+logw2+…logwk。这样就将乘法转变为了加分,为了求最长路,可以作取相反数处理。因为 w i ( 0 ≤ w i ≤ 1 ) w_i(0\leq w_i\leq1) wi(0≤wi≤1),所以 log w i ≤ 0 \log w_i \leq 0 logwi≤0,取相反数后全是非负数,所以可以采用Dijkstra算法。
代码
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 2010;
int n,m,S,T;
double g[N][N];
double dist[N];
bool st[N];
void dijkstra()
{
dist[S] = 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()
{
scanf("%d%d",&n,&m);
while(m--){
int a,b,c;
scanf("%d%d%d",&a,&b,&c);
double z = (100.0 - c) / 100;
g[a][b] = g[b][a] = max(g[a][b], z);
}
scanf("%d%d",&S,&T);
dijkstra();
printf("%.8f\n",100.0/dist[T]);
return 0;
}
收获
- 求最长路,可以将边权取反,做最短路。其实在写代码的过程中,不一定真的取反,只需要将三角不等式不等号反过来, m i n min min改成 m a x max max即可。
- 对于乘法来说,权值如果是小于等于 1 1 1,那么相当于加法的负边权;如果是大于 1 1 1的话,就是正边权。