题目链接:A Dangerous Maze (II) - LightOJ 1395 - Virtual Judge (ppsucxtt.cn)
题意:一个迷宫有n扇门,每个门对应一个非零值x,如果x是正的,就会在x分钟后带你逃离迷宫,如果x是负的,就会在(-x)分钟后带你回到原点,你会记录你最后走的k扇门,问你走出迷宫的时间期望。
这道题目是之前做过的一道题目的变形,上道题目博客链接:(58条消息) (LightOJ - 1027)A Dangerous Maze(数学期望)_AC__dream的博客-CSDN博客
之前的那道题目中你不存在记忆,而这道题目中你会对之前走过的门保留一定的记忆,这就是两道题目的不同点,我们可以设dp[i]表示你已经保留了i扇门的记忆,此时你想要出去所要花费的时间期望,需要注意的一点时,你保留的记忆一定是错误的门,因为你一旦走到了正确的门你将会直接出去而省去接下来的过程,我们先来分析一下dp数组如何更新:
于是就有动态转移方程:dp[i]=1.0*(cnt2-i)/(cnt1+cnt2-i)*dp[i+1]+1.0*(sum1+1.0*sum2/cnt2*(cnt2-i))/(cnt1+cnt2-i);
我们现在来考虑一下终止状态:
如果k>=cnt2,当我们记忆完所有的错误门时,一定有下次成功的概率为1,直接定义终止时的dp值为sum1/cnt1即可
如果k<cnt2,则当我们记忆完k扇门时,下一次一共有cnt1+cnt2-k扇门供我们选择,成功的概率为cnt1/(cnt1+cnt2-k),如果不成功概率不变,直到成功为止,所以这是一个几何分布模型,成功的期望次数就是成功概率的倒数,也就是(cnt1+cnt2-k)/cnt1,每次花费的时间是当前所有情况的平均时间,也就是(sum1+sum2/cnt2*(cnt2-k))/(cnt1+cnt2-k),每次的平均时间乘以期望次数就是期望时间了,接下来直接进行动态转移就好了,最后不要忘记特判没有正值门的情况。
下面是代码:
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<queue>
using namespace std;
const int N=200;
double f[N];
//f[i]表示已经记住i扇门后出去的期望时间
int main()
{
int T,cnt1,sum1,cnt2,sum2;
cin>>T;
for(int o=1;o<=T;o++)
{
cnt1=sum1=cnt2=sum2=0;
int n,t,k;
scanf("%d%d",&n,&k);
for(int i=1;i<=n;i++)
{
scanf("%d",&t);
if(t>0) cnt1++,sum1+=t;
else cnt2++,sum2-=t;
}
if(cnt2==n)
{
printf("Case %d: -1\n",o);
continue;
}
if(k>=cnt2) f[k=cnt2]=1.0*sum1/cnt1;
else f[k]=1.0*(sum1+1.0*sum2/cnt2*(cnt2-k))/(cnt1+cnt2-k)*(cnt1+cnt2-k)/cnt1;
for(int i=k-1;i>=0;i--)
f[i]=1.0*(cnt2-i)/(cnt1+cnt2-i)*f[i+1]+1.0*(sum1+1.0*sum2/cnt2*(cnt2-i))/(cnt1+cnt2-i);
printf("Case %d: %.10lf\n",o,f[0]);
}
return 0;
}