期望/概率dp入门+题单

期望dp几种常见设转移方程数组的方法

1、设f [ i ]表示的是由i 状态变成 最终状态的期望 (由末状态逆推)

2、按照题意直接设

3、把选择的东西加入数组,如f [ i ] [ j ]表示第i个物品选j个的期望
或f [ i ] [ j ]表示有i个A 物品,j个B物品的期望
(结合第一种的话就是,dp[i]j[j]:已经有i个A ,j个B 离达到最终状态还差多少期望)

求转移方程
先考虑逆向。(从最终状态的解开始逆推)
如果逆向没有思路,则考虑正向。

(一般而言,末状态已经决定了的话,就是逆向递推了)
也可以先看看边界在哪边,从边界开始递推转移。

例题:

Favorite Dice SPOJ - FAVDICE

题意:给定一个n面的骰子,问投出n个不同的面的期望投掷次数。 (1<=n<=1000)

分析:先考虑逆向转移,dp[n]=0 表示投出n个不同面之后,要达到投出n个不同面的状态还需要投掷0次。

即:dp[i]表示已经投出了i个面,要投出剩余n-i面的期望次数。

对于当前状态为i,投一次骰子,有i/n的可能投中已经出现的i个面之一,此情况下还需要投dp[i]次
					   有(n-i)/n的可能投出其余n-i面。此情况下还要投dp[i+1]次
即:由于投出的面可能出现过,也可能没出现过,所以dp[i]由dp[i] 与 dp[i+1] 转移而来
dp[i]=dp[i]*(i/n) + dp[i+1]*((n-i)/n) +1   (+1是因为要投一次骰子才能转移)

移项变成:dp[i]=dp[i+1] + n/(n-i)
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define pii pair<int,int>
const int maxn = 2e3+5;
const int mx = 40;
const int mod = 1e9+5;
const ll inf = 34359738370;
const int INF = 1e9+7;
//给定n面的骰子 求投的期望次数 满足每面都至少出现一次
double dp[maxn];//dp[i] 表示已经出现i个面时  投出剩余n-i面的期望次数 dp[n]=0
//再投一次 i/n的几率投中已经有的面,此情况下还需要投dp[i]次骰子,(n-i)/n几率投中新的面  此情况下还要投dp[i+1]次
//dp[i]=(i/n)dp[i] + (n-i)/n *dp[i+1] +1
//(n-i)/n * dp[i]= (n-i)/n *dp[i+1] +1
//dp[i]=dp[i+1] + n/(n-i)
int main()
{
   
    int t;scanf("%d",&t);
    while(t--)
    {
   
        int n;
        scanf("%d",&n);
        dp[n]=0.0;
        for(int i=n-1;i>=0;--i)
        {
   
            dp[i]=dp[i+1] + 1.0*n/(n-i) ;
        }
        printf("%.2f\n",dp[0]);
    }
    return 0;
}

也可以顺推,dp[i]表示投出i个面的期望次数,
dp[i] = dp[i-1] + n/(n-(i-1)) , 也就是投出i-1面的期望加上投一个新面的期望。

#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
double dp[1005];
int main()
{
   
    int T,n;
    scanf("%d",&T);
    while(T--)
    {
   
        scanf("%d",&n);
        dp[0]=0;
        dp[1]=1;
        for(int i=2;i<=n;i++)
            dp[i]=dp[i-1]+(double)n/(double)(n+1-i);
        printf("%.2f\n",dp[n]);
    }
    return 0;
}


Kids and Prizes SGU - 495

题意:n个空白格子,随机选一个涂色,一共涂m次,问最终被涂色格子数目的期望。
(1<=n,m<=1e5)

分析:这题考虑逆向的话行不通。
而第i次涂色可由第i-1次转移而来,所以正向转移特别好想。

dp[i]表示涂了i次 被涂色格子数目期望
dp[i]由dp[i-1]转移而来,第i次涂色: dp[i-1]/n的概率涂中已经被涂色的格子,这种情况下答案仍是dp[i-1]
							      (n-dp[i-1])/n的概率涂中空白格子,这种情况下答案是dp[i-1]+1
dp[i]=(dp[i-1]/n) *dp[i-1] + (n-dp[i-1])/n * (dp[i-1]+1)

细节是这题误差要求1e-9,所以记得保留9位小数点输出

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define pii pair<int,int>
const int maxn = 1e5+5;
const int mx = 40;
const int mod = 1e9+5;
const ll inf = 34359738370;
const int INF = 1e9+7;
//n个格子  每次随机给一个染色 问m次染色后被涂上色的格子期望个数
//dp[i] i次染色后 涂色格子的期望个数 dp[1]=1
//第i次染色 (dp[i-1]/n)的几率涂中已经有颜色的格子,这种情况下仍然是dp[i-1] , (n-dp[i-1])/n 的几率涂中新格子
//dp[i]=(dp[i-1]/n)*dp[i-1] + (n-dp[i-1])/n *(dp[i-1]+1)
int n,m;
double dp[maxn];
int main()
{
   
    int t;t=1;
    while(t--)
    {
   
        scanf("%d %d",&n,&m);
        dp[1]=1.0;
        double num=n*1.0;
        for(int i=2;i<=m;i++)
        {
   
            dp[i]=(dp[i-1]/num)*dp[i-1] + (num-dp[i-1])/num * (dp[i-1]+1);
        }
        printf("%.9f\n",dp[m]);
    }
    return 0;
}

King Arthur’s Birthday Celebration POJ - 3682

题意:投一个硬币,第n次投的代价是2*n-1, 投中正面朝上的概率为给定的p,
投到k次正面朝上就停止 。 问投掷次数的期望,代价的期望。

分析:这题同样从正向开始考虑

E[i]表示投到i次正面朝上的次数期望, 当前投掷有p的概率是正面,1-p的概率是反面
E[i] = p*(E[i-1]) + (1-p)*(E[i]) + 1 
化简一下就是E[i]=1/p+E[i-1]  ,即:E[i]=i/p
(也可以这样理解,1次正面朝上的期望是1/p,每次投掷都是独立事件,所以i次正面朝上的期望次数就是i*1/p)


f[i]表示i次正面朝上的代价期望。
当前投掷有p概率是正面,这种情况下 当前投掷是第dp[i-1]+1次投掷,代价是2*(E[i-1]+1)-1
有1-p的概率是反面,此时的投掷是第dp[i]+1次投掷,代价是2*(E[i]+1)-1

f[i]=p*(f[i-1]+2*(E[i-1]+1)-1) + (1-p)*(f[i]+2*(E[i]+1)-1)
化简一下就是:f[i]=(f[i-1]+2*(E[i-1]+1)-1) +(1-p)/p * ( 2*(E[i]+1)-1 )
#include<cstdio>
  • 5
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值