题解 CF605E 【Intergalaxy Trips】
\mathrm{Problem}
-
-
- 每天选择一条存在的出边走过去。
- 求最优策略下从
1 到n 的期望天数。 -
\mathrm{Solution}
搞了一天的期望神题
我们设
有一些显然但很重要的结论,就是:
- 节点
x 接下来转移的点一定是当前开放的出边中E 值最小的一个点。 - 对于若存在
x,y 满足E_x<E_y ,则x 的期望决策点一定不包含y 。
对于结论2:当
因此我们便有了算法的大致思路:每一次都寻找一个期望值最小的点,并固定这个点的期望值;同时也用这个点的期望值去更新未被更新的期望值。
该算法的思路与Dijkstra算法类似。
考虑如何转移:我们发现若我们要转移节点
观察到我们此时计算的
因此我们的期望天数就变成了
则上述转移方程就变成了:
然后就可以愉快的转移辣
\mathrm{Code}
#include <bits/stdc++.h>
using namespace std;
const int N = 2000;
int n;
int vis[N];
double s[N], P[N][N], E[N];
int read(void)
{
int s = 0, w = 0; char c = getchar();
while (!isdigit(c)) w |= c == '-', c = getchar();
while (isdigit(c)) s = s*10+c-48, c = getchar();
return w ? -s : s;
}
int main(void)
{
n = read();
for(int i=1;i<=n;++i)
for (int j=1;j<=n;++j)
P[i][j] = 1.0 * read() / 100.0;
if (n == 1) return puts("0"), 0;
vis[n] = 1, E[n] = 0;
for (int i=1;i<=n;++i) {
E[i] = 1;
s[i] = 1 - P[i][n];
}
for (int i=1;i<=n;++i)
{
int p = 0;
double Min = 1e18;
for (int j=1;j<=n;++j)
if (vis[j] == 0 && 1.0 * E[j] / (1 - s[j]) < Min)
Min = 1.0 * E[j] / (1 - s[j]), p = j;
vis[p] = 1;
if (p == 1) return printf("%.15lf\n", Min) * 0;
for (int j=1;j<=n;++j)
E[j] += 1.0 * E[p] / (1 - s[p]) * P[j][p] * s[j], s[j] *= 1.0 - P[j][p];
}
return 0;
}