【CF605E】Intergalaxy Trips(期望)
- 给定一张\(n\)个点的有向完全图,其中\(x\rightarrow y\)的边每天有\(a_{x,y}\)的概率出现(保证\(\forall x,a_{x,x}=1\))。
- 每天需要从当前点选择一条存在的出边,走到对应的点。
- 求采取最优策略时,从\(1\)号点走到\(n\)号点的期望天数。
- \(n\le10^3\)
期望真是一个玄学的东西,感觉总是不知道啥可以啥不可以。。。
期望
这道题中,我们设\(E(x)\)表示采取最优策略时,从\(x\)走到\(n\)的期望步数。
一个显然的结论,若\(E(x)<E(y)\),则我们不会选择从\(x\)走到\(y\)。(因为这样肯定会让答案变得更大)
如果要利用这个性质,从\(1\)号点出发正着做明显不太好求出\(E(x)\)(答案就是\(E(1)\)啊,若能直接求出还有什么好做的。。。),因此我们要从\(n\)号点出发倒着做。
具体操作流程类似于\(Dijkstra\),每次选出\(E(x)\)最小的点去更新其他点的。不过由于这是一张完全图,堆优化没有意义,直接暴力做即可。
但还有一个问题没解决,如何维护、计算\(E(x)\)。
\(E(x)\)的计算
假设对于一个点\(x\),已知有\(k\)个点\(y_1,y_2,...,y_k\)满足\(E(y_1)\le E(y_2)\le...\le E(y_k)\le E(x)\)。
贪心地去考虑,必然是尽可能走到\(y_1\),次优是走到\(y_2\),以此类推。
于是我们就可以用一个式子来表示\(s_x\)(可以理解为离开\(x\)到\(n\)的期望天数,但有一点区别,后面会提到):
因为我们具体操作时肯定是每次找到新的\(y_k\)去更新每一个\(s_x\)的,所以我们只要维护好\(p_x=\prod_{i=1}^k(1-a_{x,y_k})\)(\(p_x\)的实际意义是不会离开\(x\)的概率),则一次更新就变成了:
然后考虑\(E(x)\)的计算,我们只需分是否离开两种转移:(注意\(s_x\)本身就包含了\(1-p_x\),无需再乘上这个系数)
式子两边都有\(E(x)\),经典的移项转化,得到:
知道了\(E(x)\)的计算,这道题也就做完了。
代码:\(O(n^2)\)
#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 1000
using namespace std;
int n,vis[N+5];double s[N+5],p[N+5],a[N+5][N+5];
int main()
{
#define E(x) ((s[x]+p[x])/(1-p[x]))//计算x到n的期望天数
#define F5(x) vis[x]=1;for(RI i=1;i<=n;++i)\
!vis[i]&&(s[i]+=(E(x)+1)*p[i]*a[i][x],p[i]*=1-a[i][x]);//用当前最优点x更新剩余点
RI i,j,t;for(scanf("%d",&n),i=1;i<=n;++i)
for(j=1;j<=n;++j) scanf("%lf",a[i]+j),a[i][j]/=100;
for(i=1;i^n;++i) p[i]=1;F5(n);for(i=1;i<=n&&!vis[1];++i)
{for(t=0,j=1;j<=n;++j) !vis[j]&&(!t||E(t)>E(j))&&(t=j);F5(t);}//每次选取最优点转移
return printf("%.10lf\n",E(1)),0;
}