【CF605E】Intergalaxy Trips(贪心,动态规划)
题面
Codeforces
洛谷
有\(n\)个点,每个时刻第\(i\)个点和第\(j\)个点之间有\(p_{ij}\)的概率存在一条边。每个时刻可以沿着一条边走或者留在原地。求从\(1\)号点走到\(n\)号点的最优的期望时间。
题解
设\(E(x)\)表示从\(x\)走到\(n\)的最短期望时间,那么考虑当前停的这个点的下一步应该怎么走,首先,你一定会走向当前能够到达的所有点中,\(E(x)\)最小的那个,而如果所有可以到达的点的\(E(x)\)都大于当前点的期望,那么一定会留在原地。
抓住这样一个性质:我们不会走向期望比当前点的期望更加大的点。那么我们从小往大依次考虑出所有点的期望。这样子每次拓展一个点的复杂度就是\(O(n)\)的,一共拓展\(n\)轮,所以总的复杂度就是\(O(n^2)\)。
转移大概是这样的,我们假装所有后继都是按照\(E(x)\)从小往大枚举的。
\(E(x)=\sum_{i}E(i)*p_{xi}\prod_{j=1}^{i-1}(1-p_{xj})\)
即考虑所有可以选择的后继,因为我们肯定当前出现的所有边中,期望最小的那个走,所以当前这条边被选定的概率就是前面所有边都没有被选中的概率乘上这条边出现了的概率。
那么,我们考虑每次选定的最小期望,显然不会用比他大的期望去更新他,既然它是当前最小,那么所有未确定的点的期望都不可能用来更新这个点,所以当前确定的就一定是这个点。
注意一点,如果你确定了一个点之后,当前得到的这个值并不是实际的期望,考虑用来更新的那些边都没有出现,这个的概率是\(\prod (1-p_{xj})\),那么这么多的概率你会停留在原地,把它减过去再除过来,即还要除掉一个\(1-\prod p\)才是最终的期望。
#include<iostream>
#include<cstdio>
using namespace std;
#define ll long long
#define MAX 1010
inline int read()
{
int x=0;bool t=false;char ch=getchar();
while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
if(ch=='-')t=true,ch=getchar();
while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
return t?-x:x;
}
int n,Q[MAX],h,t;
bool use[MAX],vis[MAX];
double E[MAX],p[MAX][MAX],prod[MAX];
int main()
{
n=read();
for(int i=1;i<=n;++i)
for(int j=1;j<=n;++j)
p[i][j]=read()/100.00;
if(n==1){puts("0");return 0;}
for(int i=1;i<=n;++i)E[i]=1,prod[i]=1-p[i][n];
E[n]=0;vis[n]=true;
for(int i=1;i<=n;++i)
{
int u=0;double mn=1e18;
for(int j=1;j<=n;++j)
if(!vis[j]&&E[j]/(1-prod[j])<mn)
u=j,mn=E[j]/(1-prod[j]);
vis[u]=true;if(u==1){printf("%.10lf\n",E[1]/(1-prod[1]));return 0;}
for(int j=1;j<=n;++j)
E[j]+=(E[u]/(1-prod[u]))*p[j][u]*prod[j],prod[j]*=(1-p[j][u]);
}
return 0;
}