扑克牌【概率DP】【状态表示:每种类有多少个】

本文探讨了一个涉及扑克牌的概率DP问题,即求解获得指定数量各花色牌所需翻牌期望值。通过定义状态转移方程,考虑大、小王的最优使用策略,最终实现了问题的有效解决。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

概率DP

要抽象出:起点、中点

转移的时候:一个点转移到n个点,然后记忆化

题目

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≤A,B,C,D≤150≤A,B,C,D≤15

输入样例:
1 2 3 4
输出样例:
16.393

扑克牌的期望DP

  • 状态转移:抽出一张牌放入分类
  • 状态表示:每种牌有多少个,大王、小王代替什么牌
  • 转移:按照当前取的牌放到哪种,可以由一个状态变为多个状态
  • 起点:当前没有拿牌
  • 终点:把牌放完,终点概率为0
  • 拿一张牌的期望为1,在此基础上+=其他可以变化的状态
#include<iostream>
#include<map>
#include<cmath>
#include<string.h>
using namespace std;
typedef long long ll;
const int N=17;
const double INF=1e20;

double f[N][N][N][N][5][5];
int A,B,C,D;

double dp(int a,int b,int c,int d,int x,int y)
{
    double &v = f[a][b][c][d][x][y];
    if(v>=0) return v;
   
    int sa=a+(x==0) + (y==0);
    int sb=b+(x==1) + (y==1);
    int sc=c+(x==2) + (y==2);
    int sd=d+(x==3) + (y==3);
    
    if(sa>=A && sb>=B && sc >=C && sd >=D) return 0;
    int sum = sa + sb+ sd+sc;
    //int sum=a+b+c+d+(x!=4) + (y!=4);
    sum=54-sum;
    if(sum<=0) return INF;
    v=1;// 转移的权重就是1(翻一张卡牌)
    if( a<13) v+=  (13.0-a)/sum*dp(a+1,b,c,d,x,y) ;
    if( b<13) v+=  (13.0-b)/sum*dp(a,b+1,c,d,x,y) ;
    if(c<13) v+=   (13.0-c)/sum*dp(a,b,c+1,d,x,y) ;
    if(d<13) v+=   (13.0-d)/sum*dp(a,b,c,d+1,x,y) ;
    if(x==4)
    {
        double minv=INF;
        for(int i=0;i<4;i++)
            minv=min(minv, 1.0/sum*dp(a,b,c,d,i,y));
        v+=minv;
    }
    if(y==4)
    {
        double minv=INF;
        for(int i=0;i<4;i++)
            minv=min(minv,  1.0/sum*dp(a,b,c,d,x,i));
        v+=minv;
    }
    return v;
}

int main()
{
   cin>>A>>B>>C>>D;
   memset(f,-1,sizeof f);
   double t=dp(0,0,0,0,4,4);
   if(t>=INF/2) cout<<"-1.000"<<endl;
   else printf("%.3lf\n" ,t);
    
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值