版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/Ronaldo7_ZYB/article/details/89526291
Description
Rainbow把一副扑克牌(54张)随机洗开,倒扣着放成一摞。然后Admin从上往下依次翻开每张牌,每翻开一张黑桃、红桃、梅花或者方块,就把它放到对应花色的堆里去。
Rainbow想问问Admin,得到A张黑桃、B张红桃、C张梅花、D张方块需要翻开的牌的张数的期望值E是多少?
特殊地,如果翻开的牌是大王或者小王,Admin将会把它作为某种花色的牌放入对应堆中,使得放入之后E的值尽可能小。
由于Admin和Rainbow还在玩扑克,所以这个程序就交给你来写了~
题目大意
有13,13,13,13,1,1的6堆扑克牌;每一可以从中选出一张牌;求到达最终状态下,选择牌数的最小期望值。
题解
这是一个最优值问题,我们可以思考使用 来解决这一个问题。
状态: 表示剩下 张黑桃, 张红桃, 张梅花, 张方块,大王的状态,小王的状态的最小期望值。其中 表示没有被选择, 分别表示选择了黑桃、红桃、梅花和方块。f同理。
目标状态:
sum表示总牌
然后就有很复杂的状态转移方程了:
对于实现的方式,我们可以使用记忆化搜索来实现。
有几个需要注意的细节:
- 无解:如果A,B,C,D中大于十三的总牌数大于2,说明大王和小王补不齐,一定无解。
- 对于记忆化里的状态:如果所有的牌已经达到要求,返回0即可。
代码如下:
#include<bits/stdc++.h>
using namespace std;
int A,B,C,D;
double F[16][16][16][16][5][5];
double dfs(int a,int b,int c,int d,int e,int f)
{
if (F[a][b][c][d][e][f] > 1e-8)
return F[a][b][c][d][e][f];
if (a+(e==1)+(f==1)>=A && b+(e==2)+(f==2)>=B &&
c+(e==3)+(f==3)>=C && d+(e==4)+(f==4)>=D) return 0;
double sum,Min1,Min2;
Min1 = Min2 = 1e9, sum = 1.0;
int cnt = a+b+c+d+(e!=0)+(f!=0);
if (a<13) sum += dfs(a+1,b,c,d,e,f)*(13-a)/(54-cnt);
if (b<13) sum += dfs(a,b+1,c,d,e,f)*(13-b)/(54-cnt);
if (c<13) sum += dfs(a,b,c+1,d,e,f)*(13-c)/(54-cnt);
if (d<13) sum += dfs(a,b,c,d+1,e,f)*(13-d)/(54-cnt);
if (e == 0) for (int i=1;i<5;++i) Min1 = min(Min1,dfs(a,b,c,d,i,f)/(54-cnt));
if (f == 0) for (int i=1;i<5;++i) Min2 = min(Min2,dfs(a,b,c,d,e,i)/(54-cnt));
if (e == 0) sum += Min1;
if (f == 0) sum += Min2;
return F[a][b][c][d][e][f] = sum;
}
int main(void)
{
freopen("test.in","r",stdin);
freopen("test.out","w",stdout);
cin>>A>>B>>C>>D;
if (max(A-13,0)+max(B-13,0)+max(C-13,0)+max(D-13,0) > 2)
return puts("-1.000"), 0;
double ans = dfs(0,0,0,0,0,0);
printf("%.3lf",ans);
return 0;
}