UVA1541 To Bet or Not To Bet
04 June 2019 |
duanyll | Tags:
OI
题解
概率DP
题意
给定一个线性棋盘,每回合抛一次硬币,正面向前走一个,反面向前走两个。走到的格子上面可能有三种操作或者没有操作:
- 向前走$n$格
- 向后走$n$格
- 跳过下一回合(
回合数++
)
抛完硬币后就执行一次目标格的操作,只执行一次。
问在$T$回合内走到终点或者终点之后的概率(输出格式见原题)。
分析
记忆化搜索,$dfs(i,j)$表示第$i$步走到第$j$格的概率。分类讨论抛硬币正面和反面的情况。
走一个的情况:
\[dfs(i,j)=\left\{ \begin{array}{ll} dfs(i+2,j+1) & \textrm{j+1格是停一回合}\\ dfs(i+1,j+1+offset[j+1]) & \textrm{j+1格不是停一回合} \end{array} \right.\]走两个的情况:
\[dfs(i,j)=\left\{ \begin{array}{ll} dfs(i+2,j+2) & \textrm{j+2格是停一回合}\\ dfs(i+1,j+2+offset[j+2]) & \textrm{j+2格不是停一回合} \end{array} \right.\]答案就是以上两个值的平均数。
代码
输入格式很恶心。
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <cmath>
using namespace std;
const int MAXN = 100;
const char DOUBLE_MIN = 0xc2;
const double EPS = 1e-8;
bool bad[MAXN];
int offset[MAXN];
double dp[MAXN][MAXN];
int n, t;
double dfs(int i, int j) {
if (dp[i][j] >= 0) return dp[i][j];
if (j == n + 1) return 1; // 走到了
if (i >= t) return 0; // 机会用完了没走到
dp[i][j] = 0;
if (bad[j + 1]) {
dp[i][j] += dfs(i + 2, j + 1) / 2;
} else {
dp[i][j] += dfs(i + 1, j + 1 + offset[j + 1]) / 2;
}
int to = min(n + 1, j + 2); //特判跑出去的情况
if (bad[to]) {
dp[i][j] += dfs(i + 2, to) / 2;
} else {
dp[i][j] += dfs(i + 1, to + offset[to]) / 2;
}
return dp[i][j];
}
int dcmp(double x, double y) {
double c = x - y;
if (c > EPS) return 1;
if (c < -EPS) return -1;
return 0;
}
int main() {
int tcnt;
cin >> tcnt;
for (int T = 1; T <= tcnt; T++) {
cin >> n >> t;
memset(bad, false, sizeof bad);
memset(offset, 0, sizeof offset);
memset(dp, 0xc2, sizeof dp);
for (int i = 1; i <= n; i++) {
char opr[10];
scanf("%s", opr);
if (opr[0] == 'L') {
bad[i] = true;
} else {
offset[i] = floor(atof(opr));
}
}
double ans = dfs(0, 0);
switch(dcmp(ans, 0.5)) {
case 1:
printf("Bet for. %.4lf\n", ans);
break;
case 0:
printf("Push. %.4lf\n", ans);
break;
case -1:
printf("Bet against. %.4lf\n", ans);
break;
}
}
}