题目链接:https://codeforces.com/problemset/problem/235/C
设 \(f_i\) 表示从点 \(i\) 到点 \(n\) 的期望步数。
显然当我们最终把 \(f\) 按从小到大排序之后,一定是排在后面的转移到排在前面的。
所以我们需要确定一个顺序保证转移只会从最终 \(f\) 较小的转移到 \(f\) 较大的。
不难发现在某一个状态下,肯定选择没有选择过且 \(f\) 最小的进行转移就可以满足上述条件。所以我们用类似 dij 的转移方式。
那么有
因为如果我们考虑走到 \(j\),当且仅当比它期望步数更小的我们都没法走,且我们能走向 \(j\)。
记 \(g_i\) 为截止当前转移最后一个 \(\sum\) 内的值,就可以 \(O(1)\) 转移了。
时间复杂度 \(O(n^2)\)。
#include <bits/stdc++.h>
using namespace std;
const int N=1010;
int n;
double f[N],g[N],p[N][N];
bool vis[N];
int main()
{
scanf("%d",&n);
for (int i=1;i<=n;i++)
{
f[i]=g[i]=1;
for (int j=1;j<=n;j++)
{
scanf("%lf",&p[i][j]);
p[i][j]/=100.0;
}
}
f[n]=g[n]=0;
for (int i=1;i<=n;i++)
{
int x=0;
for (int j=1;j<=n;j++)
if (!vis[j] && g[j]<1 && (!x || f[j]/(1-g[j])<f[x]/(1-g[x]))) x=j;
vis[x]=1; f[x]/=(1-g[x]);
for (int j=1;j<=n;j++)
if (!vis[j])
{
f[j]+=f[x]*p[j][x]*g[j];
g[j]*=(1-p[j][x]);
}
}
printf("%.10lf",f[1]);
return 0;
}
原文:https://www.cnblogs.com/stoorz/p/14270197.html