Code Jam 2008 APAC local onsites Problem C. Millionaire —— 概率DP

Problem

You have been invited to the popular TV show "Would you like to be a millionaire?". Of course you would!

The rules of the show are simple:
  • Before the game starts, the host spins a wheel of fortune to determine P, the probability of winning each bet.
  • You start out with some money: X dollars.
  • There are M rounds of betting. In each round, you can bet any part of your current money, including none of it or all of it. The amount is not limited to whole dollars or whole cents.

    If you win the bet, your total amount of money increases by the amount you bet. Otherwise, your amount of money decreases by the amount you bet.

  • After all the rounds of betting are done, you get to keep your winnings (this time the amount is rounded down to whole dollars) only if you have accumulated $1000000 or more. Otherwise you get nothing.

Given MP and X, determine your probability of winning at least $1000000 if you play optimally (i.e. you play so that you maximize your chances of becoming a millionaire).

Input

The first line of input gives the number of cases, N.

Each of the following N lines has the format "M P X", where:

  • M is an integer, the number of rounds of betting.
  • P is a real number, the probability of winning each round.
  • X is an integer, the starting number of dollars.

Output

For each test case, output one line containing "Case #XY", where:

  • X is the test case number, beginning at 1.
  • Y is the probability of becoming a millionaire, between 0 and 1.

Answers with a relative or absolute error of at most 10-6 will be considered correct.

Limits

1 ≤ N ≤ 100
0 ≤ P ≤ 1.0, there will be at most 6 digits after the decimal point.
1 ≤ X ≤ 1000000

Small dataset

1 ≤ M ≤ 5

Large dataset

1 ≤ M ≤ 15

Sample


Input 

Output 
2
1 0.5 500000
3 0.75 600000

Case #1: 0.500000
Case #2: 0.843750

In the first case, the only way to reach $1000000 is to bet everything in the single round.

In the second case, you can play so that you can still reach $1000000 even if you lose a bet. Here's one way to do it:

  • You have $600000 on the first round. Bet $150000.
  • If you lose the first round, you have $450000 left. Bet $100000.
  • If you lose the first round and win the second round, you have $550000 left. Bet $450000.
  • If you win the first round, you have $750000 left. Bet $250000.
  • If you win the first round and lose the second round, you have $500000 left. Bet $500000.
这道题是一个不是很明显的概率DP,题意是你要进行M轮的赌博,一开始你有初始金额X , 你每一轮获胜的概率都为P,当你最后持有的金钱 >= 1000000时算作取胜,给你M , P , X , 让你输出取胜的概率。
首先,由于赌注随意,可以为小数,所以无法直接暴枚,这里我们就会用到一个技巧“化连续为离散”。我们从最后一轮开始分类讨论,显然,最后一轮只有三种情况0 --- 500000 , 500000 --- 1000000 , 1000000 ---  ;第一种获胜的概率为0,第二种为P,第三种为1。然后,我们再看倒数第二轮,分为5种状态:0 --- 250000 , 250000 --- 500000 , 500000 --- 750000  , 750000 --- 1000000 , 1000000 --- ;然后我们便得出第i轮的情况有2的i次幂 + 1种。然后枚举每种情况的期望,找到最优步骤即可。
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <iostream>
#include <cstring>
#include <map>
#include <string>
#include <stack>
#include <cctype>
#include <vector>
#include <queue>
#include <set>
#include <utility>
#include <cassert>
using namespace std;
///#define Online_Judge
#define outstars cout << "***********************" << endl;
#define clr(a,b) memset(a,b,sizeof(a))
#define lson l , mid  , rt << 1
#define rson mid + 1 , r , rt << 1 | 1
#define mk make_pair
#define FOR(i , x , n) for(int i = (x) ; i < (n) ; i++)
#define FORR(i , x , n) for(int i = (x) ; i <= (n) ; i++)
#define REP(i , x , n) for(int i = (x) ; i > (n) ; i--)
#define REPP(i ,x , n) for(int i = (x) ; i >= (n) ; i--)
const int MAXN = 100 + 50;
const int MAXS = 10000 + 50;
const int sigma_size = 26;
const long long LLMAX = 0x7fffffffffffffffLL;
const long long LLMIN = 0x8000000000000000LL;
const int INF = 0x7fffffff;
const int IMIN = 0x80000000;
const int inf = 1 << 30;
#define eps 1e-8
const long long MOD = 1000000000 + 7;
const int mod = 100000;
typedef long long LL;
const double PI = acos(-1.0);
typedef double D;
typedef pair<int , int> pii;
#define Bug(s) cout << "s = " << s << endl;
///#pragma comment(linker, "/STACK:102400000,102400000")
int M , X  ;
double P;
double dp[2][(1 << 15) + 1];
void solve()
{
    int n = 1 << M;
    double *pre = dp[0] , *nxt = dp[1];
    memset(pre , 0 , sizeof(double) * (n + 1));
///    memset(pre , 0 , sizeof(pre)); 这样初始化是不行的,因为pre为一个double型的指针,不是整个数组。
    pre[n] = 1.0;
    for(int r = 0 ; r < M; r++)///枚举第几轮
    {
        for(int i = 0 ; i <= n ; i++)///枚举当前是哪种状态
        {
            int step = min(i , n - i);///如果step大于n / 2 , 等会儿转移的时候可能会超过n
            double t = 0.0;
            for(int j = 0 ; j <= step ; j++)///枚举当前的所有可能走法
            {
                t = max(t , P * pre[i + j] + (1 - P) * pre[i - j]);///求出期望的最大值
            }
            nxt[i] = t;
        }
        swap(pre , nxt);///交换两个数组的值进行滚动
    }
    int i = (LL)X * n / 1000000;///找到X对应的是第几块
//    for(int i = 0 ; i <= n ; i++)cout << '*' << pre[i] << endl;
    printf("%.6lf\n" , pre[i]);
}
int main()
{
    freopen("C-large-practice.in" , "r" , stdin);
    freopen("C-large-practice.out" , "w" , stdout);
    int t;
    cin >> t;
    FORR(kase ,1 , t)
    {
        scanf("%d%lf%d" , &M , &P , &X);
        printf("Case #%d: " , kase);
        solve();

    }
    return 0;
}


你可能感兴趣的:(ACM,概率DP)