218. 扑克牌

题目链接

218. 扑克牌

Admin 生日那天,Rainbow 来找 Admin 玩扑克牌。

玩着玩着 Rainbow 觉得太没意思了,于是决定给 Admin 一个考验。

Rainbow 把一副扑克牌(\(54\) 张)随机洗开,倒扣着放成一摞。

然后 Admin 从上往下依次翻开每张牌,每翻开一张黑桃、红桃、梅花或者方块,就把它放到对应花色的堆里去。

Rainbow 想问问 Admin,得到 \(A\) 张黑桃、\(B\) 张红桃、\(C\) 张梅花、\(D\) 张方块需要翻开的牌的张数的期望值 \(E\) 是多少?

特殊地,如果翻开的牌是大王或者小王,Admin 将会把它作为某种花色的牌放入对应堆中,使得放入之后 \(E\) 的值尽可能小。

由于 Admin 和 Rainbow 还在玩扑克,所以这个程序就交给你来写了。

输入格式

输入仅由一行,包含四个用空格隔开的整数,\(A,B,C,D\)

输出格式

输出需要翻开的牌数的期望值 \(E\),四舍五入保留 \(3\) 位小数。

如果不可能达到输入的状态,输出 -1.000

数据范围

\(0 \le A,B,C,D \le 15\)

输入样例:

1 2 3 4

输出样例:

16.393

解题思路

概率dp

  • 状态表示:\(f[a][b][c][d][x][y]\) 表示当前翻开 \(a\) 张黑桃,\(b\) 红桃,\(c\) 张梅花,\(d\) 张方块,且小王的状态为 \(x\)\(x=0\) 表示选择黑桃,\(x=1\) 表示选择红桃,\(x=2\) 表示选择梅花,\(x=3\) 表示选择方块,\(x=4\) 表示未选中,大王同理),大王的状态为 \(y\) 的期望翻牌张数

  • 状态计算:这时还剩下的牌数为 \(sum=54-a-b-c-d-(x!=4)-(y!=4)\),这时再选中黑桃、红桃、梅花、方块的概率分别为 \(\frac{13-a}{sum}、\frac{13-b}{sum}、\frac{13-c}{sum}、\frac{13-d}{sum}\),相对应的状态转移,再判断这时的 \(x\)\(y\) 的状态是否使用,如果未使用的话,选择一个期望长度最小的状态转移

  • 时间复杂度:\(O(13^4\times 5^2)\)

代码

// Problem: 扑克牌
// Contest: AcWing
// URL: https://www.acwing.com/problem/content/description/220/
// Memory Limit: 64 MB
// Time Limit: 1000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

// %%%Skyqwq
#include <bits/stdc++.h>
 
//#define int long long
#define help {cin.tie(NULL); cout.tie(NULL);}
#define pb push_back
#define fi first
#define se second
#define mkp make_pair
using namespace std;
 
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<LL, LL> PLL;
 
template <typename T> bool chkMax(T &x, T y) { return (y > x) ? x = y, 1 : 0; }
template <typename T> bool chkMin(T &x, T y) { return (y < x) ? x = y, 1 : 0; }
 
template <typename T> void inline read(T &x) {
    int f = 1; x = 0; char s = getchar();
    while (s < '0' || s > '9') { if (s == '-') f = -1; s = getchar(); }
    while (s <= '9' && s >= '0') x = x * 10 + (s ^ 48), s = getchar();
    x *= f;
}

const int N=15,inf=0x3f3f3f3f;
int A,B,C,D;
double f[N][N][N][N][5][5];
double dp(int a,int b,int c,int d,int x,int y)
{
	double &res=f[a][b][c][d][x][y];
	if(res>=0)return res;
	if(a+(x==0)+(y==0)>=A&&b+(x==1)+(y==1)>=B&&c+(x==2)+(y==2)>=C&&d+(x==3)+(y==3)>=D)return res=0;
	int sum=54-a-b-c-d-(x!=4)-(y!=4);
	if(sum<=0)return res=inf;
	res=0;
	if(a<13)res+=(dp(a+1,b,c,d,x,y)+1)*(13-a)/sum;
	if(b<13)res+=(dp(a,b+1,c,d,x,y)+1)*(13-b)/sum;
	if(c<13)res+=(dp(a,b,c+1,d,x,y)+1)*(13-c)/sum;
	if(d<13)res+=(dp(a,b,c,d+1,x,y)+1)*(13-d)/sum;
	if(x==4)
	{
		double t=inf;
		t=min(t,dp(a,b,c,d,0,y));
		t=min(t,dp(a,b,c,d,1,y));
		t=min(t,dp(a,b,c,d,2,y));
		t=min(t,dp(a,b,c,d,3,y));
		res+=(t+1)/sum;
	}
	if(y==4)
	{
		double t=inf;
		t=min(t,dp(a,b,c,d,x,0));
		t=min(t,dp(a,b,c,d,x,1));
		t=min(t,dp(a,b,c,d,x,2));
		t=min(t,dp(a,b,c,d,x,3));
		res+=(t+1)/sum;
	}
	return res;
}
int main()
{
    scanf("%d%d%d%d",&A,&B,&C,&D);
    memset(f,-0x3f,sizeof f);
    double res=dp(0,0,0,0,4,4);
    if(res>inf/2)puts("-1.000");
    else
    	printf("%.3lf",res);
    return 0;
}
posted @ 2022-12-06 20:14  zyy2001  阅读(66)  评论(0编辑  收藏  举报