ACM-ICPC 2017 Asia Urumqi:A. Coins(DP)

挺不错的概率DP,看似基础,实则很考验扎实的功底

这题很明显是个DP,为什么???找规律或者算组合数这种概率,N不可能给的这么友善。。。

因为DP一般都要在支持N^2操作嘛。

稍微理解一下,这DP[i][j]还是不好想啊,首先是写DP[I][j]的含义

首先我们想这道题是要求一个最优决策下的期望,那么这个我们的最优决策是什么???

决策就是:我们假设我这一次需要翻转K个硬币,我们不愿翻那些已经在正面的,而去翻那些没有在正面的

而如果剩余的反面的不足,我再去翻转正面的

那么给dp[i][j]一个含义,代表我现在进行第i轮,已经翻转了j个正面了,并用一个K表示我当前这一轮有K个正面朝上,再写出转移方程

dp[i+1][j+k]=dp[i][j]*C(z,i)*pow(0.5,z);

C(z,k)*pow(0.5,z);就代表,这一次需要在Z个硬币中,翻转上来K个的概率

而如果出现剩余面不足,我翻转反面,相减就可以

注意POW会超时,写一个p[i]的数组,表示(1/2)^i就可以了

#include<iostream>
#include<string.h>
#include<stdio.h>
#include<algorithm>
#include<math.h>
using namespace std;
double c[204][204];
double dp[204][204];
double p[204];
int main()
{
    int n,m,k;
    int t,z;
    scanf("%d",&t);
    int tmp1,tmp2,tmp3;
    c[0][0]=1;
    for (int i=1;i<=100; i++)
    {
        c[i][0]=1;
        for (int j=1;j<=i;j++)
        {
            c[i][j]=c[i-1][j-1]+c[i-1][j];
        }
    }
    p[0]=1;
    for (int i=1;i<=100;i++){
        p[i]=p[i-1]*0.5;
    }
    while(t--)
    {
        scanf("%d%d%d",&n,&m,&z);
        memset(dp,0,sizeof(dp));
        dp[0][0]=1;
        for (int i=0; i<m; i++)//本轮
        {
            for (int j=0; j<=n; j++)//已经有j面向下了
            {
                if(dp[i][j]==0)continue;
                for (int k=0; k<=z; k++)//如果在这t枚中得到了k个向下的
                {
                       if (j+z<=n)//全选面向下的
                          dp[i+1][j+k]+=dp[i][j]*p[z]*c[z][k];
                       else    //选完剩余的还要选已经向上的
                          dp[i+1][k+n-z]+=dp[i][j]*p[z]*c[z][k];
                }
            }
        }
        double ans=0;
        for (int i=1;i<=n;i++){
            ans+=dp[m][i]*i;
        }
        printf("%.3lf
",ans);
    }
    return 0;
}
有不懂欢迎咨询 QQ:1326487164(添加时记得备注)
原文地址:https://www.cnblogs.com/bluefly-hrbust/p/9515303.html