当前位置: 代码迷 >> 综合 >> Tyvj P2002 扑克牌
  详细解决方案

Tyvj P2002 扑克牌

热度:48   发布时间:2023-12-13 19:00:47.0

背景
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。

输入

样例输入1
1 2 3 4

样例输入2
15 15 15 15

输出

样例输出1
16.393

样例输出2
-1.000

备注
对于100%的数据,0<=A,B,C,D<=15

此题只需分类讨论+记忆化搜索。。。
f[a][b][c][d][x][y]来记录当前已经翻了a张黑桃,b张红桃,c张梅花,d张 方片,小王状态为x,大王状态为y时的期望值。x=4表示没有用过小王, x=0~3表示用过小王且变成相应的数。
边界:已经翻出的牌不少于给定的个数,返回0。
转移:
如果不翻开王,则F+=f(a+1,b,c,d,x,y)*(13-a)/(54-sum),b,c,d同理; 否则枚举王翻成何种牌,F+=Min{f(a,b,c,d,0~3,y)/(54-sum)}

#include<iostream>
#include<cstdio>
using namespace std;
int x1,x2,x3,x4;
double ans,dp[15][15][15][15][5][5];
bool vis[15][15][15][15][5][5];
double dpp(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];if(a+(x==0)+(y==0)>=x1&&b+(x==1)+(y==1)>=x2&&c+(x==2)+(y==2)>=x3&&d+(x==3)+(y==3)>=x4)return dp[a][b][c][d][x][y]=0;int sum=a+b+c+d+(x!=4)+(y!=4);//已取张数 double s=1,tmp;if(a<13)s+=dpp(a+1,b,c,d,x,y)*(13-a)/(54-sum);if(b<13)s+=dpp(a,b+1,c,d,x,y)*(13-b)/(54-sum);if(c<13)s+=dpp(a,b,c+1,d,x,y)*(13-c)/(54-sum);if(d<13)s+=dpp(a,b,c,d+1,x,y)*(13-d)/(54-sum);if(x==4){tmp=1e9;for(int i=0;i<=3;i++)tmp=min(tmp,dpp(a,b,c,d,i,y)/(54-sum));s+=tmp;}if(y==4){tmp=1e9;for(int i=0;i<=3;i++)tmp=min(tmp,dpp(a,b,c,d,x,i)/(54-sum));s+=tmp;}vis[a][b][c][d][x][y]=1;return dp[a][b][c][d][x][y]=s;
}
int main()
{scanf("%d%d%d%d",&x1,&x2,&x3,&x4);ans=dpp(0,0,0,0,4,4);if(ans>54)ans=-1;//期望值超出达不到 return printf("%.3f\n",ans),0;//Tyvj的个性。。。 
}