AcWing 218 扑克牌
题意:54张牌,从中连续不断的取卡,得到 A 张黑桃 、B 张红桃、C 张梅花、D 张方块需要翻开的牌的张数的期望值 E 是多少?
如果翻开的是王牌,会作为放入后使E最小的扑克牌放入。
题解:递归求解数学期望,因为抽卡一定是一张于是在进行到某一步时的期望等价于抽到各种牌的概率相加,于是只需要计算出抽到各个牌的概率即可,这很简单,对于一张牌,假设已经抽到的牌为cnt张,而当前牌已经使用a张,那么抽到这张牌的概率为\(\frac{13 - a}{54 - cnt}\)而这个概率还需要不断的递进求解期望,特殊的对于抽到王牌的情况,我们尝试将其放入每一种牌中,并取其期望最小值,于是便可以得到转移公式
解题代码:
#include <bits/stdc++.h>
using namespace std;
const int N = 15;
double dp[N][N][N][N][5][5];
int vis[N][N][N][N][5][5];
int A, B, C, D;
double dfs(int a,int b,int c,int d,int x,int y){
//记忆搜索剪枝
if(vis[a][b][c][d][x][y])return dp[a][b][c][d][x][y];
vis[a][b][c][d][x][y] = 1;
//检查是都已经到达终点
bool fa,fb,fc,fd;
fa = fb = fc = fd = false;
if(a + (x == 1) + (y == 1) >= A)fa = true;
if(b + (x == 2) + (y == 2) >= B)fb = true;
if(c + (x == 3) + (y == 3) >= C)fc = true;
if(d + (x == 4) + (y == 4) >= D)fd = true;
if(fa && fb && fc && fd)return 0;
//以下是没有结束时每一位的取法
int cnt = a + b + c + d + (x != 0) + (y != 0);//已经使用的牌的数目
double sum = 1.0;
if(a < 13)sum += dfs(a + 1, b, c, d, x, y) * (13 - a) / (54 - cnt);//下一抽是花色a的概率(因为抽牌数固定为1,因此概率P即为期望)
if(b < 13)sum += dfs(a, b + 1, c, d, x, y) * (13 - b) / (54 - cnt);//下一抽是花色b的概率
if(c < 13)sum += dfs(a, b, c + 1, d, x, y) * (13 - c) / (54 - cnt);//下一抽是花色c的概率
if(d < 13)sum += dfs(a, b, c, d + 1, x, y) * (13 - d) / (54 - cnt);//下一抽是花色d的概率
//没有抽到第一张王牌,那么这张王牌会被用于补充最少的牌
if(x == 0){
double mi = 1e18;
for(int i = 1;i <= 4;i++){
mi = min(mi,dfs(a, b, c, d, i, y) / (54 - cnt));
}
sum += mi;
}
//没有抽到第二张王牌
if(y == 0){
double mi = 1e18;
for(int i = 1;i <= 4;i++){
mi = min(mi,dfs(a, b, c, d, x, i) / (54 - cnt));
}
sum += mi;
}
return dp[a][b][c][d][x][y] = sum;
}
int main(){
cin >> A >> B >> C >> D;
if(max(A - 13,0) + max(B - 13,0) + max(C - 13,0) + max(D - 13,0) > 2){
cout << "-1.000\n";
return 0;
}
double ans = dfs(0,0,0,0,0,0);
printf("%.3f\n",ans);
return 0;
}