题面
思路
假设起点为 S S S,终点为 T T T;
题目想问 S S S最少是多少;
在满足 S ∗ w 1 ∗ w 2 ∗ . . . = T = 100 S*w_1*w_2*...=T=100 S∗w1∗w2∗...=T=100的情况下;
其中 w i w_i wi是边权;
要使得 S S S最少,说明 w 1 ∗ w 2 ∗ . . . w_1*w_2*... w1∗w2∗...要最大;
即 S m i n = 100 ( w 1 ∗ w 2 ∗ . . . ) m a x S_{min} = \frac{100}{(w_1*w_2*...)_{max}} Smin=(w1∗w2∗...)max100
那现在的问题就转为求起点到终点的一条乘积最大值;
接着我们要证明乘积最大值是可以套加法最小值的最短路算法;
证明
我们给
w
1
∗
w
2
∗
.
.
.
w_1*w_2*...
w1∗w2∗...取个log
;
求 l o g ( w 1 ∗ w 2 ∗ . . . ) = l o g ( w 1 ) + l o g ( w 2 ) + . . . log(w_1*w_2*...)=log(w_1)+log(w_2)+... log(w1∗w2∗...)=log(w1)+log(w2)+...最大;
因为
w
i
w_i
wi是小于等于
1
1
1的,因此取log
后就全是非正数;
因此我们将每个数取相反数,问题就变成了单源最短路问题;
注意
因为这道题 0 < w i ≤ 1 0<w_i ≤1 0<wi≤1,因此由上述证明处转化以后,边权是非负的;
因此我们可以用迪杰斯特拉
,如果没有限制
w
i
w_i
wi,那么就可能出现负权边,就不能用迪杰斯特拉
了;
我用的是SPFA
;
Code
#include <iostream>
#include <cstdio>
#include <queue>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
const int N = 2e3 + 10;
int n,m,start,over;
//G(i,j)为0表示没有路 刚好不用初始化
//因为我们是乘积
double G[N][N];
double dist[N];
bool in_que[N];
void spfa(){
queue<int> que;
que.push(start);
dist[start] = 1;//乘积的"0"是1 加法的"0"是0
in_que[start] = 1;
while(!que.empty()){
int u = que.front();
que.pop();
in_que[u] = 0;
for(int to=1;to<=n;++to){
if(G[u][to] == 0) continue;
if(dist[to] < dist[u]*G[u][to]){
dist[to] = dist[u]*G[u][to];
if(!in_que[to]){
que.push(to);
in_que[to] = 1;
}
}
}
}
}
void solve(){
cin >> n >> m;
for(int i=1,u,v,z;i<=m;++i){
cin >> u >> v >> z;
//变成原来的百分之(1-z%)
G[u][v] = G[v][u] = max(G[u][v],(100.0-z)/100);
}
cin >> start >> over;
spfa();
printf("%.8lf\n",100.0 / dist[over]);
}
int main(){
std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
solve();
return 0;
}
总结(推荐使用)
加法最小值
1.无负权边:dijkstra
2.有负权边:spfa
加法最大值
spfa
乘法最小值
1.>=1:dijkstra
2.>0:spfa
乘法最大值
1.0-1:dijkstra
2.>0: spfa