upc - 5212: Coins I(概率DP)

题目描述

Alice and Bob are playing a simple game. They line up a row of n identical coins, all with the heads facing down onto the table and the tails upward.
For exactly m 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.

输入

The input has several test cases and the first line contains the integer t (1 ≤ t ≤ 1000) which is the total number of cases.
For each case, a line contains three space-separated integers n, m (1 ≤ n, m ≤ 100) and k (1 ≤ k ≤ n).

输出

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

来源/分类

ICPC2017  Urumqi 

原来还有概率dp这种东西.......

题意:

给出n个反着的硬币,m次操作,每次可以任选k个硬币抛向空中,每个落下来  正反的概率各占一半。

问,在尽量使硬币为正的策略下,最后的正面期望值。

分析:

想了很久的规律....丝毫没有考虑到dp...

首先,最优策略就是:每次选取k个的时候,我们先选反面朝上的硬币,如果不够,再选正面朝上的

因为每次的翻转只跟上次的翻转情况有关,所以我们可以推导一下递推式dp

很容易想到dp[i][j] 表示 翻转完第i次时,j个硬币正面朝上的概率

递推式:我们得到了dp[i][j] 第 i 次翻转后有 j 个正面朝上,这时候我们进行第 i+1 次翻转,会遇到两种情况:

  1. j+k<=n: 即我们可以选k个反面朝上的硬币操作,然后可以求得这k个硬币中朝上的个数 t 的概率p,那么dp[i+1][j+t] = dp[i][j]*p;    p = C(k,t)* (1/2)^k
  2. j+k > n: 即需要选择 tp=(j+k-n) 个正面朝上的硬币进行翻转,所以第i+1次翻转后确定的正面朝上的个数为 j-tp 个,然后考虑k个刚翻转的正面朝上的个数t 的概率p,p还是C(k,t)* (1/2)^k;所以此时递推式为 dp[i+1][j-tp+t] = dp[i][j]*p;

而且:对于给定的k,t 的概率p恒为 C(k,t)* (1/2)^k,所以可以先预处理这一部分

AC代码:

#include <bits/stdc++.h>
#define inf 0x3f3f3f3f
#define linf 0x3f3f3f3f3f3f3f3fLL
#define pi acos(-1.0)
#define ms(a,b) memset(a,b,sizeof(a))
using namespace std;
typedef long long ll;
ll qpow(ll x, ll y, ll mod){ll s=1;while(y){if(y&1)s=s*x%mod;x=x*x%mod;y>>=1;}return s;}

int n, m, k;
double dp[105][105];
double C[105][105], p[105];

void init()
{
    C[0][0] = 1; p[0] = 1;
    for(int i=1;i<105;i++)
    {
        C[i][0]=1;
        p[i]=p[i-1]*0.5;
        for(int j=1;j<=i;j++)
            C[i][j]=C[i-1][j]+C[i-1][j-1];
    }
    for(int i=0;i<=k;i++) C[k][i]*=p[k];
}
int main()
{
    int T; scanf("%d", &T);
    while(T--)
    {
        scanf("%d%d%d", &n, &m, &k);
        init(); ms(dp, 0);
        dp[0][0] = 1;
        for(int i=0;i<m;i++)
        {
            for(int j=0;j<=n;j++)
            {
                if(j+k<=n)
                {
                    for(int t=0;t<=k;t++)
                        dp[i+1][j+t] += dp[i][j]*C[k][t];
                }
                else
                {
                    int tp=j+k-n;
                    for(int t=0;t<=k;t++)
                        dp[i+1][j+t-tp] += dp[i][j]*C[k][t];
                }
            }
        }
        double ans = 0;
        for(int i=1; i<=n; i++)
            ans += i*dp[m][i];

        printf("%.3f\n", ans);
    }

    return 0;
}

猜你喜欢

转载自blog.csdn.net/Vitamin_R/article/details/81944259