• AcWing 218. 扑克牌


    根据书上的提示这是一道数学期望和动态规划结合的题目

    扑克牌是由各种花色各(13)张+带小王组成的,总共由(54)张牌组成

    (f[a][b][c][d][x][y])表示已经拿到了(a)张黑桃,(b)张红桃,(c)张梅花,(d)张方块,小王和大王的状态分别是(x,y)其中(x=0)表示放到黑桃里,(1)表示放到红桃里,(2)表示放到梅花里,(3)表示放到方块里,(4)则表示没有拿到((y)同理)的期望翻开次数

    我们可以通过计算轻松获得到当前的翻到的牌的数量:(sum=a+b+c+d+(x==4)+(y==4))

    (update):上面写错了,应该是(x!=4和y!=4),淦

    剩下的牌的数量是(Sum=54-sum)

    现在事情变得通透了起来,淦

    以黑桃为例,剩余黑桃的数量为(13-a),那么下一张牌翻到黑桃的概率就是(frac{13-a}{Sum}(Sum eq 0))

    那么对答案的贡献就是(frac{13-a}{Sum}*f[a+1][b][c][d][x][y])(反着来递推)

    其他的花色类似,这里就不在赘述了

    再来看带小王的情况

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

    我们还是按照上面花色的分析思路,以小王为例

    剩余的小王数量就是(1-(x==4)),剩余的牌的数量仍是(Sum)

    显然抽到小王的概率就是(frac{1-(x==4)}{Sum}(Sum eq 0)),貌似只有当(x=4)的情况才有贡献,所有不用考虑其他情况,淦

    如果抽到小王,我们把它作为某种花色的牌放入对应堆中,使得放入之后E的值尽可能小。

    所以小王的贡献就是(frac{1}{Sum}min_{0 le i le 3}{f[a][b][c][d][i][y]})

    大王情况类似,这里就不在赘述了

    综上转移方程就是

    [f[a][b][c][d][x][y]=frac{13-a}{Sum}*f[a+1][b][c][d][x][y]+frac{13-b}{Sum}*f[a][b+1][c][d][x][y]+frac{13-c}{Sum}*f[a][b][c+1][d][x][y]+frac{13-d}{Sum}*f[a][b][c][d+1][x][y]+min_{0 le i le 3}{f[a][b][c][d][i][y]}(x==4)+min_{0 le j le3}{f[a][b][c][d][x][j]}(y==4) ]

    边界问题:

    • 如果已经翻开的牌的数量答案的题目的要求,则期望为(0),例如(a+(x==0)+(y==0))

      • 注意这个条件应该是所有翻开的牌都满足,就是说条件使用&&链接的,淦
    • (54)张牌被翻完但是仍无法到岸,即(54-sumle0),则期望为(infty)

    目标:

    (f[0][0][0][0][4][4])

    #include<bits/stdc++.h>
    using namespace std;
    const int N=15;
    double f[N][N][N][N][5][5];
    int v[N][N][N][N][5][5];
    int A,B,C,D;
    double solve(int a,int b,int c,int d,int x,int y)
    {
    	if(v[a][b][c][d][x][y]) return f[a][b][c][d][x][y];
    	v[a][b][c][d][x][y]=1;
    	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 0;
    	double sum=1.0;
    	int cnt=a+b+c+d+(x!=4)+(y!=4);
    	if(a<13) sum+=solve(a+1,b,c,d,x,y)*(13-a)/(54-cnt);
    	if(b<13) sum+=solve(a,b+1,c,d,x,y)*(13-b)/(54-cnt);
    	if(c<13) sum+=solve(a,b,c+1,d,x,y)*(13-c)/(54-cnt);
    	if(d<13) sum+=solve(a,b,c,d+1,x,y)*(13-d)/(54-cnt);
    	if(x==4)
    	{
    		double minn1=1e9;
    		for(int i=0;i<=3;++i) minn1=min(minn1,solve(a,b,c,d,i,y)/(54-cnt));
    		sum+=minn1;
    	}
    	if(y==4)
    	{
    		double minn2=1e9;
    		for(int j=0;j<=3;++j) minn2=min(minn2,solve(a,b,c,d,x,j)/(54-cnt));
    		sum+=minn2;
    	}
    	return f[a][b][c][d][x][y]=sum;
    }
    int main()
    {
    	cin>>A>>B>>C>>D;
    	if(max(A-13,0)+max(B-13,0)+max(C-13,0)+max(D-13,0)>2){
    		puts("-1.000");
    		return 0;
    	}
    	double ans=solve(0,0,0,0,4,4);
    	printf("%.3f",ans);
    	return 0;
    }
    

    [写题一时爽,调题火葬场 ]


  • 相关阅读:
    java基础面试题(一)
    关于Redis的十个高频面试问题
    postgres中的merge join
    cgdb调试postgresql
    再见了,程序员!
    掌握这个小技巧,让你的 C++ 编译速度提升 50 倍!
    C语言修仙大法!基础知识全复习(纯干货)!!!
    一锅端了!北京朝阳一互联网公司被端,警方上门,23人被带走…
    C++入口不是main?知乎上都快打起来了,你们不要再打了啦!
    不用创建项目,直接在 VS 里快速测试 C/C++ 代码 !
  • 原文地址:https://www.cnblogs.com/pyyyyyy/p/12759030.html
Copyright © 2020-2023  润新知