CF605E Intergalaxy Trips 【贪心,动态规划,期望】
有一个 \(n\) 个点的有向完全图,每个时刻 \(i\) 向 \(j\) 有 \(p_{i,j}\) 的概率存在一条边。每个时刻可以走一条边或留在原地,求从 \(1\) 到 \(n\) 的最优期望时间。
\(n\le 10^3\)
用类似 dijkstra 的方法,按期望时间从小到大更新别的点,\(E_x\) 表示从 \(x\) 走到 \(n\) 的最短期望时间。
\[E_x=\sum_iE_ip_{xi}\prod_j(1-p_{xj})
\]
(取当前出现的所有边中期望最小的那个走)
然而其实你是要去掉一条边都走不出去的概率的,也就是除以 \(1-\prod_i(1-p_{xi})\),其中 \(i\) 是目前能用于更新的边的另一个端点。
#include<bits/stdc++.h>
#define Rint register int
using namespace std;
template<typename T>
inline void read(T &x){
int ch = getchar(); x = 0;
for(;ch < '0' || ch > '9';ch = getchar());
for(;ch >= '0' && ch <= '9';ch = getchar()) x = x * 10 + ch - '0';
}
template<typename T>
inline bool chmax(T &a, const T &b){if(a < b) return a = b, 1; return 0;}
template<typename T>
inline bool chmin(T &a, const T &b){if(a > b) return a = b, 1; return 0;}
const int N = 1003;
int n;
double p[N][N], prod[N], E[N];
bool vis[N];
int main(){
read(n);
for(Rint i = 1;i <= n;++ i)
for(Rint j = 1;j <= n;++ j)
read(p[i][j]), p[i][j] /= 100;
if(n == 1) return puts("0"), 0;
for(Rint i = 1;i <= n;++ i){
E[i] = 1; prod[i] = 1 - p[i][n];
}
E[n] = 0; vis[n] = true;
for(Rint i = 1;i <= n;++ i){
int u = 0; double tmp = 1e18;
for(Rint j = 1;j <= n;++ j)
if(!vis[j] && chmin(tmp, E[j] / (1 - prod[j]))) u = j;
vis[u] = true;
if(u == 1) return printf("%.6lf\n", tmp), 0;
for(Rint j = 1;j <= n;++ j){
E[j] += tmp * p[j][u] * prod[j];
prod[j] *= 1 - p[j][u];
}
}
}