比较简单的概率DP,也是场上出的第一道概率DP题,感觉和普通DP没有什么本质上的区别,只不过是用来求概率的 。
所以需要注意的还是那么几个 : 定义顺序(阶段)、弄清每一个阶段要产生几个决策(决定了该阶段的概率)、当前最优解依赖于已经求出的部分最优解、具有相似的最优子结构(可能有点抽象,不过确实如此)、处理好边界条件 ect
值得一提的是该题必须用滚动数组,不然会超内存(第一次就超了),借此机会也让我终于深刻理解了紫书上讲解滚动数组的那一段话 。
由于每一次的最优解依赖的只是他上一层的最优解,所以再之前的解已经没用了,因此只要保存两维,将其他的解覆盖掉就行了。循环滚动两行的值,所以一叫滚动数组。
代码如下:
#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
int n, m, l ,r;
const int maxm=1000005;
const int maxn=205;
double dp[2][maxn];
int a[maxm];
int main(){
while(~scanf("%d%d%d%d", &n, &m, &l, &r) && m+n+l+r){
for(int i =1; i<=m;++i) scanf("%d", &a[i]);
memset(dp, 0, sizeof(dp)); //初始化边界,除了d[0][1] = 1; 其他都是 0
dp[0][1] = 1;
int u=0;
for(int i =1; i<=m;++i){
u ^= 1;
for(int j=1; j<=n;++j){
dp[u][j]=0;
int right=(j+a[i]);
while(right>n) right-=n;
int left=(j-a[i]);
while(left<=0) left+=n;
dp[u][j]+=(dp[1-u][left]/2);
dp[u][j]+=(dp[1-u][right]/2);
}
}
double pp=0;
for(int i=l;i<=r;++i){
pp+=dp[m%2][i];
}
printf("%.4f\n", pp);
}
return 0;
}