概率DP【入门】

HDU 3076 ssworld VS DDD

 

题意: 2个人分别有AB的血数,轮流扔骰子,数小的自减一血,平的不变,谁先到减0, 谁输,问A赢的概率。

题解: 考虑平局的出现对局面没有影响,因此把平局规约到非平局里即可,对于每一次p1表示A赢,p2表示B赢,p=1-p1-p2表示平局,A赢的概率为p1+p*p1+p^2*p1+…p^n*p1,n->无穷,即a=q1/(1-p);b=q2/(1-p);

然后在他们一定会分出胜负的情况下就可以dp了:

   dp[i][j]=dp[i][j-1]*a+dp[i-1][j]*b;

代码很丑,数组开的太大还MLE一次

 

#include <cstdio>
#include <cstring>

const int maxn=2000+5;
double sum[2][7];
double p[2][7] , dp[maxn][maxn];

int A,B;
double a,b;//算上平局的单次
double a1,b1;//单次

void DP()
{
    memset (dp, 0, sizeof(dp));
    a=b=a1=b1=0.0;
    for (int i=1 ; i<6 ; ++i)
    {
        a1+=sum[1][i]*p[0][i+1];
        b1+=sum[0][i]*p[1][i+1];
    }
    //printf("a==%lf , b==%lf\n", a1, b1);
    double p=1-a1-b1;
    a=a1/(1.0-p);
    b=b1/(1.0-p);
    //printf("%lf , %lf  , 平局%lf\n", a, b, p);
    dp[0][0]=1;

    for (int i=0 ; i<=A ; ++i)
    {
        for (int j=0 ; j<=B ; ++j)
        {
            if(i>0 && j<B)dp[i][j]+=dp[i-1][j]*a;
            if(j>0 && i<A)dp[i][j]+=dp[i][j-1]*b;
        }
        //printf("\n");
    }

    double ans=0;
    for (int i=0 ; i<B ; ++i)
    {
        ans+=dp[A][i];
    }
    printf("%lf\n",ans);
}

/*
5 5
0.500 0.500 0.000 0.000 0.000 0.000
0.000 0.000 0.000 0.000 0.000 1.000
5 5
0.000 0.000 0.000 0.000 0.900 0.100
0.000 0.000 0.000 0.000 0.900 0.100
*/

int main ()
{
    while (~scanf("%d%d",&A,&B))
    {
        sum[0][0]=sum[1][0]=0;
        for (int i=1 ; i<=6 ; ++i)
        {
            scanf("%lf", p[0]+i);
            sum[0][i]=sum[0][i-1]+p[0][i];
        }
        for (int i=1 ; i<=6 ; ++i)
        {
            scanf("%lf", p[1]+i);
            sum[1][i]=sum[1][i-1]+p[1][i];
        }
        DP();
    }

    return 0;
}
【------------------------------------------------------】
 

m道题,t个队伍,n个最少题数,给出t*m的概率,表示队伍做出题的概率。求所有队伍都至少出1题,且至少有一个队伍出n道题以上概率。

#include <cstdio>
#include <cstring>

double dp[35][35];
double sum[3];

int main ()
{
    int m,t,n;
    double p;
    while (~scanf("%d%d%d", &m, &t, &n) , (m||n||t))
    {
        double ans=1.0,ans2=1.0;
        for (int i=0 ; i<t ; ++i)
        {
            dp[0][0]=1;
            for (int j=1 ; j<=m ; ++j)
            {
                scanf("%lf", &p);
                for (int k=0 ; k<=j  && k<=m ; ++k)
                    dp[k][j]=dp[k][j-1]*(1-p)+(k?dp[k-1][j-1]*p:0);
            }
            
            sum[0]=dp[0][m];
            sum[1]=0.0;
            for (int j=1 ; j<n ; ++j)sum[1]+=dp[j][m];
            ans*=(1-sum[0]);
            ans2*=sum[1];
        }
        printf("%.3lf\n",ans-ans2);
    }
    return 0;
}


 【---------------------------------------------------】

 

HDU 3853 (2011夏)北邮邀请赛 i题

错误代码:

正向推的公式是错的, 原因是用错了无穷级数。

#include <cstdio>
#include <cstring>

const int maxn=1000+12;
const double inf=20000000.0;
using namespace std;

double map[maxn][maxn][3], dp[maxn][maxn];
///dp[i][j]=(dp[i-1][j]+1)*map[i-1][j][2]+(dp[i][j-1]+1)*map[i][j-1]+(dp[i][j]+1)*map[i][j]
int n, m;
double tmp;
int main ( )
{
    while (~scanf("%d%d", &n, &m))
    {
        dp[1][1]=0.;
        for (int i=1 ; i<=n ; ++i)
        {
            for (int j=1 ; j<=m ; ++j)
            {
                tmp=0.0;
                scanf("%lf%lf%lf", map[i][j]+0, map[i][j]+1, map[i][j]+2);
                if(map[i][j][0]!=1.0)
                {
                    if(i)tmp+=(dp[i-1][j]+1)*map[i-1][j][2];
                    if(j)tmp+=(dp[i][j-1]+1)*map[i][j-1][1];
                    tmp+=map[i][j][0];
                    tmp/=(1-map[i][j][0]);
                }
                else tmp=0.;
                dp[i][j]=tmp;
                ///printf("%d %d %lf\n", i, j, dp[i][j]);
            }
        }
        dp[n][m]=map[n-1][m][2]*(dp[n-1][m]+1)+map[n][m-1][1]*(dp[n][m-1]+1);
        printf("%.3lf\n", dp[n][m]*2);
    }
    return 0;
}
/*
2 2
0.00 0.50 0.50    0.50 0.00 0.50
0.50 0.50 0.00    1.00 0.00 0.00
3 2
0.00 0.50 0.50    0.50 0.00 0.50
0.00 0.50 0.50    0.50 0.00 0.50
0.50 0.50 0.00    1.00 0.00 0.00
*/

 

改成向前推就好了。

在2维状态下递推期望 , 利用期望的公式,

E = x1 * p1 + x2 * p2 + …… xn*pn。

状态转移方程式:
f[i][j] =(f[i][j]+2)* exp[i][j][1] + (f[i+1][j]+2) * exp[i][j][2] + (f[i][j+1]+2) * exp[i][j][3]
这个式子左右都有f[i][j],化简后:
f[i][j] =((f[i+1][j]+2) * exp[i][j][2] + (f[i][j+1]+2) * exp[i][j][3]+ 2 * exp[i][j][1])/ (1 - exp[i][j][1])


 

#include <cstdio>
#include <cstring>

const int maxn=1000+12;
using namespace std;

double map[maxn][maxn][3], dp[maxn][maxn];
double tmp;
int main ()
{
    while (~scanf("%d%d", &n, &m))
    {
        for (int i=1 ; i<=n ; ++i)
        {
            for (int j=1 ; j<=m ; ++j)
            {
                scanf("%lf%lf%lf", map[i][j]+0, map[i][j]+1, map[i][j]+2);
            }
        }
        memset (dp, 0, sizeof(dp));
        for (int i=n ; i>0 ; --i)
        {
            for (int j=m ; j>0 ; --j)
            {
                if(i==n && j==m)continue;
                if(map[i][j][0]!=1.)
                {
                    tmp=1+dp[i][j+1]*map[i][j][1]+dp[i+1][j]*map[i][j][2];
                    tmp/=(1-map[i][j][0]);
                }
                dp[i][j]=tmp;
            }
        }
        printf("%.3lf\n", dp[1][1]*2);
    }
    return 0;
}


 

 之后要刷的题:
 

 HDU 4050 ///  bupt 现场赛 I题 POJ3071 1202

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值