题目
题目链接:https://codeforces.com/problemset/problem/235/C
- (n) 个点的有向完全图。
- (i o j) 的边每天出现的概率均为 (p_{i,j}),若 (i = j),有 (p_{i,j} = 1)。
- 每天选择一条存在的出边走过去。
- 求最优策略下从 (1) 到 (n) 的期望天数。
- (n le 10^3)。
思路
设 (f_i) 表示从点 (i) 到点 (n) 的期望步数。
显然当我们最终把 (f) 按从小到大排序之后,一定是排在后面的转移到排在前面的。
所以我们需要确定一个顺序保证转移只会从最终 (f) 较小的转移到 (f) 较大的。
不难发现在某一个状态下,肯定选择没有选择过且 (f) 最小的进行转移就可以满足上述条件。所以我们用类似 dij 的转移方式。
那么有
[f_i=sum^{}_{f_j<f_i}f_j imes p_{i,j} imes sum^{}_{f_k<f_j}(1-p_{i-k})
]
因为如果我们考虑走到 (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;
}