• 取球


    Portal --> ???(这是一道。。没有来源的题==)

    Description

    ​  有一个透明的袋子,里面有(R)个红球(B)个蓝球,两种球除了颜色以外没有任何区别,一开始会先随机从袋子里面取走(m)个球,球的颜色只有游戏结束之后才知道,接着两个人轮流从袋子里面取球,球的颜色只有取出来之后才知道,每次取球数量在(1sim min(n,)剩余球数())之间,取出最后一个红球的人输,求两人都采取最优策略的情况下先手获胜概率

    ​  数据范围:(1<=R,B<=100,1<=n<=10,0<=m<=R-1)
    ​​  

    Solution

    ​  (这题其实是特别暴力地将所有的局面的概率dp出来。。嗯。。但是因为我推的式子比较麻烦所以写的题解有点长。。应该有一些。。更加简便的方法)

    ​​  化简一下条件,考虑输的情况总共有两种:

    ​​  1、取完(m)个球之后没有红球剩下(然而实际上因为数据范围的限制这种情况是不会发生的qwq)

    ​  2、决策完之后没有红球剩下

    ​ ​  

    ​​  上面两个都与“是否有红球剩下”有关,所以考虑将这个东西的概率表示出来

    ​​  记(g[r][b][m])表示从有(r)个红球,(b)个蓝球的袋子里面取(m)个球出来后,袋子里面还有红球的概率(具体一点就是假设我取出来的(m)个球的颜色分布为(k)个红球,(m-k)个蓝球,(k<r)的概率)

    ​​  那么可以得到:

    [g[r][b][m]=frac{r}{r+b}*g[r-1][b][m-1]+frac{b}{r+b}*g[r][b-1][m-1] ]

    ​​  看一下这个“先随机取出(m)个球“的操作,我们考虑将这个问题稍微转化一下:假设已经钦定了一个取球序列(包括顺序和博弈过程),那么“从袋子里取球”的操作其实就相当于从序列头取球,那么一开始拿走(m)个球对应的就是序列的前(m)项,最终的答案应该是所有可能产生的序列得到的先手胜的数量之和/序列数量之和

    ​​  我们考虑将每一个可能产生的序列的前(m)项都移到最后面去,能够得到的新的序列集合是与原来的序列集合一样的,每种情况的出现概率也相同,这个时候就变成了“两人轮流取球,最后剩(m)个球的时候停止”,也就是说我们可以将这个取(m)个球的操作放到最后,顺序并不影响结果,所以这个时候我们就可以直接从后面的局面转移了

    ​​  用(f[r][b])表示袋子里面有(r)个红球,(b)个蓝球,取走(m)个未知的球之后的先手的胜率

    ​​  可以得到:

    [f[r][b]=max(sumlimits_{j=0}^{i}frac{inom r j inom b {i-j}}{inom {r+b} i}*(1-f[r-j][b-(i-j)])*frac{g[r-i][b-(i-j)][m]}{g[r][b][m]}) ]

    ​​  其中(1<=i<=n)

    ​  具体一点就是:

    ​  ​  枚举当前的决策,(i)表示取的总球数,(j)是表示取出来的红球的个数

    ​​  ​  因为是要采取最优策略所以是所有的情况中取(max)

    ​  ​  然后前面的组合数表示的是(i)个球中颜色分布为(j)个红球(i-j)个蓝球的概率

    ​  ​  然后(1-f[r-j][b-(i-j)])表示的是下一个人面对这样的局面并且失败的概率

    ​​  ​  最后面的那个是为了保证取完这(i)个球之后,袋子里面还有剩余的红球

    ​ ​  

    ​​  具体解释一下最后的那个分数:这是一个条件概率,首先先手没有凉的一个大前提就是取完(m)个球之后还有红球剩余,然后在这个基础上,还要满足在当前决策完了之后,也就是(r-i)个红球(b-(i-j))个蓝球这样的局面下取完(m)个球也有红球剩余

    ​​  换句话来说就是假设我们已经钦定了最后取的(m)个球中有(k)个红球(下面将满足条件(x)的情况数量记为(cnt(x))),(g[r][b][m])的含义可以理解为(frac{cnt(k<r)}{all})(g[r-j][b-(i-j)][m])的含义可以理解为(frac{cnt(k<r-j)}{all}),而我们这里需要的概率应该是(frac{cnt(k<r-j)}{cnt(k<r)}),所以就是两个式子相除就好了

    ​​  那么最后的答案就是(f[R][B]),总的时间复杂度(O(RB(n^2+m)))

    ​​  写的时候要。。注意一下边界

    ​​  最后就是。。其实我们会发现(C(200,99))的时候这个组合数会爆long double,然而实际上这个时候它会保留(33)位的有效数字,虽然说并不能够精确地存储整数,但始终可以保证前几位是精确的,那么对于我们这题来说还是足够的(这个时候应该疯狂膜拜sk qwq)
    ​  

    ​  代码大概长这个样子

    #include<iostream>
    #include<cstring>
    #include<cstdio>
    #define ldb long double
    using namespace std;
    ldb g[110][110][110],f[110][110],C[210][210];
    ldb sum;
    int n,m,R,B;
    
    int main(){
    #ifndef ONLINE_JUDGE
    	freopen("a.in","r",stdin);
    #endif
    	scanf("%d%d%d%d",&R,&B,&n,&m);
    	C[0][0]=1;
    	for (int i=1;i<=200;++i){
    		C[i][0]=1; C[i][i]=1;
    		for (int j=1;j<i;++j)
    			C[i][j]=C[i-1][j]+C[i-1][j-1];
    	}
    
    	for (int r=1;r<=R;++r){
    		for (int b=0;b<=B;++b){
    			g[r][b][0]=1;
    			for (int k=1;k<=m&&k<=r+b;++k){
    				g[r][b][k]=1.0*r/(1.0*(r+b))*g[r-1][b][k-1];
    				if (b)
    					g[r][b][k]+=1.0*b/(1.0*(r+b))*g[r][b-1][k-1];
    			}
    		}
    	}
    	for (int r=1;r<=R;++r){
    		f[r][0]=0;
    		for (int b=max(m-r,0);b<=B;++b){
    			f[r][b]=0;
    			for (int i=1;i<=n&&i<=r+b;++i){
    				sum=0;
    				for (int j=0;j<=i&&j<=r;++j){
    					if (b<(i-j)) continue;
    					sum+=C[r][j]*C[b][i-j]/C[r+b][i]*(1-f[r-j][b-(i-j)])*g[r-j][b-(i-j)][m]/g[r][b][m];
    				}
    				f[r][b]=max(f[r][b],sum);
    			}
    		}
    	}
    	printf("%.10Lf
    ",f[R][B]);
    }
    
  • 相关阅读:
    Mysql里的isnull(),ifnull(),nullif
    懒加载数据
    MyEclipse编辑xml文件没有提示
    java-五子棋游戏源码
    Java版打字练习游戏源码
    Wpf实现图片自动轮播自定义控件
    WP8.1开发:自定义控件
    简单的UIButton按钮动画效果ios源码下载
    自定义的一款选项卡ios源码
    Aisen微博应用源码完整版
  • 原文地址:https://www.cnblogs.com/yoyoball/p/9544388.html
Copyright © 2020-2023  润新知