【暖*墟】#动态规划# 期望DP的学习与练习

【一. 期望DP的初步介绍】

 

  • 数学期望 P=Σ每一种状态*对应的概率。
  • 大多数题 手动找公式 或者 DP推出 即可。
  • 处理好边界写好方程,代码超级简短。

 

【注意】与常规的求解不同,数学期望经常 逆向推出

      常规的dp[x]可能表示 到了x这一状态有多少,最后答案是dp[n]

      而数学期望的dp[x]一般表示 到了x这一状态还差多少,最后答案是dp[0]

 

【二. 期望DP的简单习题】

 

(1)Uva12230 Crossing Rivers

 

题意:
有个人每天要去公司上班,每次会经过N条河,家和公司的距离为D,默认在陆地的速度为1,
给出N条河的信息,包括起始坐标p,宽度L,以及船的速度v。船会往返在河的两岸,人到达河岸时,
船的位置是随机的(往返中)。问说人达到公司所需要的期望时间。
思路:

1,过每条河最坏的情况是t=3*L/v; 即去的时候船刚刚走。 2,过每条河最优的情况是t=L/v; 即去的时候船刚刚来。 3,由于船是均匀发布的,符合线性性质,所以平均下来,过每条河的时间t=2*L/v。

 

#include <cmath>
#include <iostream>
#include <cstdio>
#include <string>
#include <cstring>
#include <vector>
#include <algorithm>
#include <stack>
#include <queue>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;

/*【uva12230】 Crossing Rivers
上班每次会经过N条河,家和公司的距离为D。在陆地的速度为1。
给出N条河的信息,包括起始坐标p,宽度L,以及船的速度v。
人到达河岸时,船的位置是随机的。问人达到公司所需要的期望时间。 */

/*【超级神奇啊】过河最坏的情况是t=3*L/v,最优的情况是t=L/v。
由于船是均匀发布的,符合线性性质,所以平均下来,过每条河的时间t=2*L/v。*/

void reads(int &x){
    int fx=1;x=0;char s=getchar();
    while(s<'0'||s>'9'){if(s=='-')fx=-1;s=getchar();}
    while(s>='0'&&s<='9'){x=(x<<3)+(x<<1)+(s-'0');s=getchar();}
    x=x*fx;//正负号
}

int main(){ 

    int n,d,p,l,v,kase=0;
    
    while(scanf("%d%d",&n,&d)&&d){
        double ans=0; for(int i=1;i<=n;i++){
            reads(p),reads(l),reads(v);
            d-=l; ans+=2.0*l/v; //记得陆地距离要减河宽
        } printf("Case %d: %.3lf\n\n",++kase,(double)ans+d); 
    }
}
【uva12230】 Crossing Rivers //巧妙

 

 


 

 

(2)SPOJ1026 Favorite Dice【赠券收集问题】

 

题意:

甩一个n面的骰子,问每一面都被甩到的次数期望是多少。
思路:

初始化 dp[n]=0; dp[i]=i/n*dp[i]+(n-i)/n*dp[i+1]+1; 化简逆推即可。答案为dp[0]。

 

#include <cmath>
#include <iostream>
#include <cstdio>
#include <string>
#include <cstring>
#include <vector>
#include <algorithm>
#include <stack>
#include <queue>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;

/*【sp1026】Favorite Dice 
一个n面的骰子,求期望掷几次能使得每一面都被掷到。*/

/*【赠券收集问题】已有i种不同的数,从集合中任选一个数得到新数的概率为n−i+1/n。
(结论)那么期望为:1/p=n/n−i+1。所以总期望为∑(i=1~n)n/n−i+1=∑(i=1~n)n/i。
----> 设f[i]表示取了i种时,还需要取的数的期望次数。显然f[n]=0,逆推,答案为f[0]。
又:由于选第i个数后再选一个数 与已经选过的数不同 的概率为n−i/n,相同为i/n。
可得:f[i]=(n−i)/n*f[i+1]+i/n*f[i]+1; 化简:f[i]=f[i+1]+n/(n−i)。f[0]=∑(i=1~n)n/i。*/

// 其实最后答案就变成了调和级数:∑(i=1~n)n/i。

void reads(int &x){
    int fx=1;x=0;char s=getchar();
    while(s<'0'||s>'9'){if(s=='-')fx=-1;s=getchar();}
    while(s>='0'&&s<='9'){x=(x<<3)+(x<<1)+(s-'0');s=getchar();}
    x=x*fx;//正负号
}

int main() 
 { int T,n; double ans; reads(T); while(T--){ ans=1,reads(n); 
     for(int i=1;i<n;i++) ans+=1.0*n/(n-i); printf("%.2lf\n",ans); } }
【sp1026】Favorite Dice //【赠券收集问题】

 

#include <cmath>
#include <iostream>
#include <cstdio>
#include <string>
#include <cstring>
#include <vector>
#include <algorithm>
#include <stack>
#include <queue>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;

/*【uva10288】优惠券 // 期望dp赠券收集问题 + 分数处理
每张彩票上面都有一种图案,共有n种,问在平均情况下最少需要买多少张彩票才能集齐n种。*/

/*【赠券收集问题】已有i种不同的数,从集合中任选一个数得到新数的概率为n−i+1/n。
(结论)那么期望为:1/p=n/n−i+1。所以总期望为∑(i=1~n)n/n−i+1=∑(i=1~n)n/i。
----> 设f[i]表示取了i种时,还需要取的数的期望次数。显然f[n]=0,逆推,答案为f[0]。
又:由于选第i个数后再选一个数 与已经选过的数不同 的概率为n−i/n,相同为i/n。
可得:f[i]=(n−i)/n*f[i+1]+i/n*f[i]+1; 化简:f[i]=f[i+1]+n/(n−i)。f[0]=∑(i=1~n)n/i。*/

// 其实最后答案就变成了调和级数:∑(i=1~n)n/i。

const int N=59;

ll lef[N+1],up[N+1],down[N+1];

void pre_cal(){
    memset(lef,0,sizeof(lef));
    memset(up,0,sizeof(up));
    memset(down,0,sizeof(down));
    lef[1]=1; for(int i=2;i<=N;i++){
        ll d1=1,d2=1; //d1为分子,d2为分母
        for(int j=2;j<=i;j++){
            d1=d1*j+d2; d2*=j; //先计算所有的答案
            ll p=__gcd(d1, d2); d1/=p; d2/=p;
        } d1*=i; ll p=__gcd(d1, d2);
        d1/=p; d2/=p; //约分
        lef[i]=d1/d2,up[i]=d1%d2,down[i]=d2;
    }
}

int main(){
    pre_cal(); int n;
    while(~scanf("%d",&n)){
        if(up[n]==0){ printf("%lld\n",lef[n]); continue; }
        for(ll i=lef[n];i>0;i/=10) printf(" ");
        printf(" %lld\n",up[n]); //分子
        printf("%lld ",lef[n]); //左部
        for(ll i=down[n];i>0;i/=10) printf("-"); printf("\n");
        for(ll i=lef[n];i>0;i/=10) printf(" ");
        printf(" %lld\n",down[n]); //分母
    }
}
【uva10288】优惠券 // 期望dp赠券收集问题 + 分数处理

 


 

 

(3)SGU495 Kids and Prizes

 

题意:

有n个奖品,m个人排队来选礼物,每个人打开的盒子,可能有礼物,也有可能已经被之前的人取走了。
打开之后,拿走礼物,把盒子放回原处。求最后m个人取走礼物的期望。
思路:

dp[1]=1; 后面的人 dp[i] = P(取到礼物盒子) + dp[i-1] = (n-dp[i-1])/n + dp[i-1]; 当然,也可以化简为公式,直接 printf("%.10lf\n",n*1.0*(1-pow((n-1)*1.0/n,m))) 。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

                                                                     ——时间划过风的轨迹,那个少年,还在等你

posted @ 2019-03-09 12:00  花神&缘浅flora  阅读(289)  评论(0编辑  收藏  举报