期望dp概率dp练习

博客内容介绍了几个使用概率动态规划(dp)解决的博弈问题,包括Discovering Gold、Aeroplane chess、Race to 1 Again和The Moon。通过逆推期望,计算在不同情况下达到目标状态的期望值或操作次数。每个问题都提供了思路解析和代码示例。
摘要由CSDN通过智能技术生成

1.Discovering Gold

https://vjudge.net/contest/306858#problem/B
题意
有n个格子,每个格子上都有一定的黄金值;还有一个色子(1-6)。起始位置站在格子1上面,若每次投掷色子得到数x,x+i<=n(i表示现处位置的格子编号),则可以到达(x+i)格子上;反之,再进行一次投掷。问:到达标号为n的格子上面,得到黄金的期望值是多少?
思路
逆推期望,dp[i]表示现在你在i号,到终点所能得到黄金的期望(一般求期望都这样设),则dp[n]为a[n](a[i]表示i号格子黄金值),dp[n-1]=1.0 * dp[n]+a[n-1],dp[n-2]=0.5 * dp[n-1]+0.5*dp[n]+a[n-2],这样往前推就可以了。距终点大于6的格子,概率都为1.0/6.0。
代码

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
int a[110];
double dp[110];
int main()
{
    int t,n,cnt=0;
    scanf("%d",&t);
    while(t--)
    {
        cnt++;
        memset(dp,0,sizeof(dp));
        scanf("%d",&n);
        for(int i=1; i<=n; i++)
            scanf("%d",&a[i]);
        dp[n]=(double)a[n];
        for(int i=n-1; i>=1; i--)
        {
            int tmp=n-i;
            if(tmp<6)
            {
                double p=1.0/(double)tmp;
                for(int j=1; j<=tmp; j++)
                    dp[i]+=p*dp[i+j];
                dp[i]+=(double)a[i];
            }
            else
            {
                for(int j=1; j<=6; j++)
                    dp[i]+=dp[i+j]/6.0;
                dp[i]+=(double)a[i];
            }
        }
        printf("Case %d: %.12f\n",cnt,dp[1]);
    }
    return 0;
}

2.hdu 4405 Aeroplane chess

http://acm.hdu.edu.cn/showproblem.php?pid=4405
题意
在0-n格子上掷色子,从0点出发,掷了多少前进几步,同时有些格点直接相连,即若a,b相连,当落到a点时直接飞向b点。求走到n或超出n期望掷色子次数
思路
还是逆推期望,dp[n]=0,dp[n-1]=dp[n]/6.0+dp[n+1]/6.0+…+dp[n+5]/6.0+1…
若在i格子可以瞬移到a[i],则dp[i]=dp[a[i]]。从后往前推的,dp[a[i]]肯定已经求完。
代码

#include <iostream>
#include <cmath>
#include <cstdio>
#include <cstring>
using namespace std;
double dp[100020];
int a[100020];
int main()
{
    int n,m,x,y;
    while(~scanf("%d%d",&n,&m))
    {
        memset(a,0,sizeof(a));
        memset(dp,0,sizeof(dp));
        if(!n&&!m)
            break;
        for(int i=0; i<m; i++)
        {
            scanf("%d%d",&x,&y);
            a[x]=y;
        }
        for(int i=n-1; i>=0; i--)
        {
            if(a[i])
                dp[i]=dp[a[i]];
            else
            {
                for(int j=1; j<=6; j++)
                    dp[i]+=dp[i+j]/6.0;
                dp[i]+=1.0;
            }
        }
        printf("%.4f\n",dp[0]);
    }

    return 0;
}

3.Race to 1 Again

https://vjudge.net/contest/306858#problem/C
题意
给一个数n,不断选择他的一个因子除以这个因子,直到除成1,问期望的操作次数,共1e4组数据,1<=n<=1e5
思路
自己独立做出来的第一道概率dp题,虽然很水,但高兴啊~~~
从特殊情况开始(开始或结束),e[1]=0,e[2]=1.0/2.0+1.0/2.0(1.0+e[2]),打个表,t组数据直接输出就可以了
解释一下e[2],2有1和2两个因子,选其中一个的概率是1.0/2.0,如果选2,则一次成功;如果选1,则又变回2,所以期望为(e[2]+1)
公式e[i]=(num+ans)/(num-1),num因子数,ans除去1和本身的因子和
代码

#include <iostream>
#include <cstdio>
#include <cmath>
using namespace std;
double e[100005];
void init()
{
    double ans,num;
    e[1]=0.0;
    for(int i=2; i<=100000; i++)
    {
        ans=0.0;
        num=2.0;
        int tmp=sqrt(i);
        for(int j=2; j<=tmp; j++)
        {
            if(i%j==0)
            {
                ans+=(e[j]+e[i/j]);
                num+=2.0;
            }
        }

        if(tmp*tmp==i)
        {
            ans-=e[tmp];
            num--;
        }
        e[i]=(ans+num)/(num-1.0);
    }
}
int main()
{
    init();
    int t,n;
    int cnt=0;
    scanf("%d",&t);
    while(t--)
    {
        cnt++;
        scanf("%d",&n);
        printf("Case %d: %.8f\n",cnt,e[n]);
    }
    return 0;
}

4.hdu 6558 2018吉林ccpc The Moon

http://acm.hdu.edu.cn/showproblem.php?pid=6558
题意
1.起初物品掉落率q=2%;
2.玩家进行一轮游戏,有p的概率获胜;
3.如果获胜了,有q的概率掉落一个叫Beta Pack的物品,终止;如果没得到Beta Pack,更新q=min(1,q+2%),返回2;
4.如果没获胜,更新q=min(1,1+1.5%),返回2;

求得到一个Beta Pack物品,进行游戏场次的期望。
题解
这题首先要分清,获胜和得到物品这两个事件,p是获胜概率,q是获胜之后得到物品的概率。
还是逆推概率,dp[i]表示q=i时还需进行的场次。
有p的概率获胜,当q=1时,只要获胜即可得到物品,那么有dp[1]=p+(1.0-p)*(dp[1]+1),移项得dp[1]=1.0/p
dp[i]后继状态有三种情况,pq的概率获胜并得到物品结束,p(1-q)的概率获胜但没得到物品更新q为min(1,q+2%),(1-p)的概率没获胜更新q为min(1,q+1.5%),可得方程
dp[i] = pq + p(1-q)(dp[i+2%]+1) + (1-p)(dp[i+1.5%]+1)
因为i代表概率,可以乘1000化成整数算。[1000,1020)区间先初始化,或者在dp方程中用min函数限制也可以。for循环,从后往前推就可以了,
代码

#include <iostream>
#include <cstdio>
using namespace std;
double dp[1100];
int main()
{
    int t,cnt=0;
    double p;
    scanf("%d",&t);
    while(t--)
    {
        cnt++;
        scanf("%lf",&p);
        p/=100.0;
        for(int i=1000; i<=1020; i++)
            dp[i]=1.0/p;
        for(int i=999; i>=20; i--)
        {
            dp[i]=p*(double)i/1000.0+(dp[i+20]+1)*p*(1.0-(double)i/1000.0)+(dp[i+15]+1)*(1.0-p);
        }
        printf("Case %d: %.10lf\n",cnt,dp[20]);
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值