- 给定一张(n)个点的有向完全图,其中(x ightarrow y)的边每天有(a_{x,y})的概率出现(保证(forall x,a_{x,x}=1))。
- 每天需要从当前点选择一条存在的出边,走到对应的点。
- 求采取最优策略时,从(1)号点走到(n)号点的期望天数。
- (nle10^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)的期望天数,但有一点区别,后面会提到):
[s_x=sum_{i=1}^k(E(y_i)+1) imes a_{x,y_i} imesprod_{j=1}^{i-1}(1-a_{x,y_j})
]
因为我们具体操作时肯定是每次找到新的(y_k)去更新每一个(s_x)的,所以我们只要维护好(p_x=prod_{i=1}^k(1-a_{x,y_k}))((p_x)的实际意义是不会离开(x)的概率),则一次更新就变成了:
[s_x exttt{+=}(E(y)+1) imes a_{x,y} imes p_x\p_x exttt{*=}1-a_{x,y}
]
然后考虑(E(x))的计算,我们只需分是否离开两种转移:(注意(s_x)本身就包含了(1-p_x),无需再乘上这个系数)
[E(x)=p_x(E(x)+1)+s_x
]
式子两边都有(E(x)),经典的移项转化,得到:
[E(x)=frac{s_x+p_x}{1-p_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
",E(1)),0;
}