扑克牌 题解

扑克牌 题解

题意

$\ \ \ \ \ \ $ 有一副扑克牌共 \(54\) 张,包括黑桃、红桃、梅花和方块四种花色各 \(13\) 张与 \(2\) 张大小王。将这副牌反面朝上放置,每次翻一张牌,若翻到大小王可以将其视作任何花色。求最后翻出 \(A\) 张黑桃、\(B\) 张红桃、\(C\) 张梅花和 \(D\) 张方块的最小期望翻牌次数是多少。


题解

\(~~~~\) 计黑桃为第一种花色,红桃为第二种花色,梅花为第三种花色,方块为第四种花色。

\(~~~~\) 一道定义为入门期望DP的题,虽然我当时想了挺久。

\(~~~~\) 显然DP至少会定义四个维度表示四种花色拥有的张数。考虑如何处理大小王。

\(~~~~\) 因此定义 \(dp_{a,b,c,d,x,y}\) ,表示翻开 \(a\) 张黑桃、\(b\) 张红桃、\(c\) 张梅花、\(d\) 张方块,且小王用作第 \(x\) 种花色,大王用作第 \(y\) 种花色后凑到要求的种类的期望翻牌最小张数。当 \(x=0\) 时表示未翻开小王, \(y=0\) 时表示为翻开大王。

\(~~~~\) 考虑一个已知的 \(dp_{a,b,c,d,x,y}\) 能扩展出哪些状态。

\(~~~~\) 以黑桃为例,当你有 \(a\) 张黑桃,已经翻开 \(k\) 张牌时,有 \(\dfrac{13-a}{54-k}\) 的概率再翻出一张黑桃。

\(~~~~\) 今天学到了一个结论,当某事件发生的概率为 \(p\) 时,则其期望在 \(\dfrac{1}{p}\) 次实验后第一次出现该事件。具体可以看 知乎上一个回答 (蒟蒻的我只会用结论)。所以就会在期望 \(\dfrac{54-k}{13-a}\) 次翻牌后第一次翻到一张黑桃。所以当你再翻出来一张黑桃时,状态就变成了 \(dp_{a+1,b,c,d,x,y}\) 。由期望的性质 \(\operatorname{E(XY)=E(X) \times E(Y)}\) 可以得到 \(dp_{a+1,b,c,d,x,y}\) 一定有贡献来自 \(dp_{a,b,c,d,x,y} \times \dfrac{54-k}{13-a}\) .另外三种花色也是同理。

\(~~~~\) 再来考虑双王的问题,若一个状态 \(dp_{a,b,c,d,0,y}\) ,此时小王未被翻出来,因此已经翻开 \(k\) 张牌时,有 \(\dfrac{1}{54-k}\) 的概率翻出小王。我们按照上面的方式化简,得到 \(dp_{a,b,c,d,x,y}\) 也有来自 \(dp_{a,b,c,d,0,y}\times (54-k)\) 的贡献,其中 \(x\in[1,4]\)

\(~~~~\) 再来考虑初始状态,可以发现当四种花色及双王满足条件时,其 \(dp\) 值为 \(0\) ,而要求的答案就是 \(dp_{0,0,0,0,0,0}\) ,因此本题是逆推。

\(~~~~\) 既然如此,则每个 \(dp_{a,b,c,d,x,y}\) 一定依托于多翻一张牌的状态。将上面所有式子移项即可。当考虑双王时,可以得到 \(\dfrac{dp_{a,b,c,d,x,y}}{54-k}\ (1\le x\le 4)\) 都有可能是 \(dp_{a,b,c,d,0,y}\) 的一部分加数。因为求最小期望,我们取四个值得最小值即可。

\(~~~~\) 综上,本题的DP转移方程是 :

\[dp_{a,b,c,d,x,y}= dp_{a+1,b,c,d,x,y}\times \dfrac{13-a}{54-sum} +dp_{a,b+1,c,d,x,y}\times \dfrac{13-b}{54-sum} +dp_{a,b,c+1,d,x,y}\times \dfrac{13-c}{54-sum} +dp_{a,b,c,d+1,x,y}\times \dfrac{13-d}{54-sum} +\min^{4}_{x=1}\{ \dfrac{dp_{a,b,c,d,x,y}}{54-sum}\}\times[x=0] +\min^{4}_{y=1}\{ \dfrac{dp_{a,b,c,d,x,y}}{54-sum}\}\times[y=0] +1 \]

\(~~~~\) 当然,每次转移前要先判断 \(a+1\),\(b+1\) 等的值是否合法。

\(~~~~\) 实现的话记忆化搜索比打递推要方便一些,这里就打的记忆化搜索。


代码

查看代码
#include <cstdio>
#include <algorithm>
#define f0 dp[a][b][c][d][x][y]
#define f1 dfs(a+1,b,c,d,x,y)
#define f2 dfs(a,b+1,c,d,x,y)
#define f3 dfs(a,b,c+1,d,x,y)
#define f4 dfs(a,b,c,d+1,x,y)
using namespace std;
double dp[15][15][15][15][5][5];
int A,B,C,D;
inline bool check(int a,int b,int c,int d,int x,int y)
{
	if(a+(x==1)+(y==1)>=A&&b+(x==2)+(y==2)>=B&&c+(x==3)+(y==3)>=C&&d+(x==4)+(y==4)>=D) return true;
	else return false;
}
double dfs(int a,int b,int c,int d,int x,int y)
{
	if(f0) return f0;
	if(check(a,b,c,d,x,y)) return 0;
	int sum=a+b+c+d+(x!=0)+(y!=0);
	double cnt=1;
	if(a<13) cnt+=f1*(13-a)/(54-sum);
	if(b<13) cnt+=f2*(13-b)/(54-sum);
	if(c<13) cnt+=f3*(13-c)/(54-sum);
	if(d<13) cnt+=f4*(13-d)/(54-sum);
	if(x==0) 
	{
		double tmp=1000;
		for(int i=1;i<=4;i++) tmp=min(tmp,dfs(a,b,c,d,i,y)/(54-sum));
		cnt+=tmp;
	}
	if(y==0)
	{
		double tmp=1000;
		for(int i=1;i<=4;i++) tmp=min(tmp,dfs(a,b,c,d,x,i)/(54-sum));
		cnt+=tmp;
	}
	return dp[a][b][c][d][x][y]=cnt;
}
int main() {
	scanf("%d %d %d %d",&A,&B,&C,&D);
	if(max(0,A-13)+max(0,B-13)+max(0,C-13)+max(0,D-13)>2)
	{
		printf("-1.000");
		return 0;
	}
	double ans=dfs(0,0,0,0,0,0);
	printf("%.3lf",ans);
	return 0;
}
posted @ 2020-08-13 20:45  Azazеl  阅读(535)  评论(0编辑  收藏  举报