题目链接
题意:每张专辑需要一定的贴纸,购买的专辑上已有的贴纸数不确定。所以需要购买,购买的包中最少有a张贴纸,最多有b张贴纸,问为了填满专辑,每个人需要贴纸数的期望值。
题解:概率dp,开始的时候以为初始的时候每个人都是0张贴纸,然后购买的包的平均值是(a+b)/2,所以很简单,直接用n*2/(a+b),后发现答案不对,重新看题后发现,每个人所拥有的初始值不确定为 0 ~ n中的任意值,所以需要用dp
因为每个人所需要的初始值不确定。所以我们设dp[i]表示当前有i张,仍然需要dp[i]个卡包,最终的答案就是dp[0]。
我们可以发现,每次都可以以1/(b-a+1)得概率拿到(a,a+1~b)个贴纸。
这样很容易推出 :
dp[i]={(dp[i+a]+1)+(dp[i+a+1]+1)+…+(dp[i+b]+1)}/(b-a+1)={dp[i+a]+dp[i+a+1]+…+dp[i+b]}/(b-a+1) + 1;
只需要维护dp[i+a]+dp[i+a+1]+…+dp[i+b]的和即可。
但是还要注意的是,因为当a=0时,后面的推导式中也会出现dp[i],所以应该提出来。
最终的推导式为:
dp[i]={dp[i+1]+dp[i+2]+…+dp[i+b]}/(b-a)+(b-a+1)/(b-a);
下面是AC代码:
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
using namespace std;
const int N=2e6+10;
double dp[N];
int main()
{
int a,b;
int n;
cin>>n>>a>>b;
dp[n]=0.0;
if(a==0)
{
double sum=0;
for(int i=n-1; i>=0; i--)
{
dp[i]=sum*1.0/(b-a)+(b-a+1)*1.0/(b-a)*1.0;
sum+=dp[i];
sum-=dp[i+b];
}
}
else
{
double sum=0;
for(int i=n-1; i>=0; i--)
{
dp[i]=sum*1.0/(b-a+1)+1;
sum+=dp[i+a-1];
sum-=dp[i+b];
}
}
printf("%.5lf",dp[0]);
return 0;
}
为什么倒推:我感觉倒推的原因一方面是边界问题,倒推不用考虑边界,另一方面是根据题设,本来就是倒推的,dp[n]是需要0个,dp[0]是需要n个。这道题问的就是需要多少个卡包,我们就设dp[i]需要几个卡包,这样设之后倒推也就是自然而然地。