2017 ICPC 乌鲁木齐现场赛 A Coin ACM-ICPC 2017 Asia Urumqi(概率dp)

题目链接

Alice and Bob are playing a simple game. They line up a row of nnn identical coins, all with the heads facing down onto the table and the tails upward.

For exactly mmm times they select any k of the coins and toss them into the air, replacing each of them either heads-up or heads-down with the same possibility. Their purpose is to gain as many coins heads-up as they can.

Input

The input has several test cases and the first line contains the integer t (1 \le t \le 1000) which is the total number of cases.

For each case, a line contains three space-separated integers n, m (1 \le n, m \le 100)and k(1 \le k \le n).

Output

For each test case, output the expected number of coins heads-up which you could have at the end under the optimal strategy, as a real number with the precision of 3 digits.

样例输入

6
2 1 1
2 3 1
5 4 3
6 2 3
6 100 1
6 100 2

样例输出

0.500
1.250
3.479
3.000
5.500
5.000

题目来源

ACM-ICPC 2017 Asia Urumqi

题意:

扫描二维码关注公众号,回复: 2781391 查看本文章

给你n个一开始朝下的硬币,然后让抛硬币,抛一次硬币,正反面朝上的概率相等。让你抛m次,每次抛k个,

一次里面抛的硬币互相独立,但不能是重复的硬币,问你,每次采取最佳策略后,朝上面的硬币的数量的期望是?

解析:

一开始做,推了半天公式,发现推出来的公式算出来的答案和样例不对........

上网搜了题解,说公式推不出来,上概率dp.......一看n,m范围.....还真行

这里主要麻烦的是,当情况①朝下的硬币数量<k时你就不得不拿朝上的硬币去抛。

这里用p表示每次抛的硬币的个数

dp[i][j]=扔了i次,j个硬币朝上的概率

那么我们就先考虑朝下的硬币数量>p的情况

dp[i][j]=\sum _{k =0}^p {dp[i-1][j-k]*C_{p}^{k}*2^{-p}}

情况②然后是朝下的硬币数量<p的情况

先看一组例子:n=6,p=4,k=3对dp[i][5]有贡献的有dp[i-1][2](情况①),dp[i-1][3](情况②),dp[i-1][4](情况②)....dp[i-1][6](情况②)

而n=6,p=4,k=4对dp[i][5]有贡献的只有dp[i-1][1](情况②)

从这组样例中我们可以看出对于一个dp[i][j],只有一个特定的k=j+p-n,才会进入情况②,并且一旦进入情况②,

dp[i][w]  w \in [n-p,n]都会对其贡献。这个可以通过公式推出来的,因为你发现后面推公式w这个变量被消掉了

w-(p-(n-w))+k=j   =>  n-p+k=j

#include <cstdio>
#include <cstring>

using namespace std;

const int MAXN = 1e2+10;
double C[MAXN][MAXN],two[MAXN];
double dp[MAXN][MAXN];

void init()
{
    two[0]=1;
    for(int i=1;i<MAXN;i++)
        two[i]=two[i-1]/2;
    C[0][0]=1;C[0][1]=0;
    for(int i=1; i<MAXN ;i++)
        for(int j=0; j<=i ;j++)
            C[i][j]=C[i-1][j-1]+C[i-1][j];
}

int main()
{
    int t;
    scanf("%d",&t);
    int n,m,p;
    init();
    while(t--)
    {
        scanf("%d%d%d",&n,&m,&p);
        dp[0][0]=1;
        for(int i=1;i<=m;i++)
            for(int j=0;j<=n;j++)
            {
                dp[i][j]=0;
           		for(int k=0;k<=p;k++)
                {
                    if(j<k) continue;
                    double ans=C[p][k]*two[p];
                    if(n-(j-k)>=p)
                    {
                    	dp[i][j]+=dp[i-1][j-k]*ans;
                    }
                }
                double ans=C[p][j+p-n]*two[p];
                for(int k=n-p+1;k<=n;k++)
                {
                    dp[i][j]+=dp[i-1][k]*ans;
                }
            }
        double res=0;
        for(int i=1;i<=n;i++)
        {
            res+=dp[m][i]*i;
        }
        printf("%.3lf\n",res);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_37025443/article/details/81564020