取球

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

Description

​  有一个透明的袋子,里面有\(R\)个红球\(B\)个蓝球,两种球除了颜色以外没有任何区别,一开始会先随机从袋子里面取走\(m\)个球,球的颜色只有游戏结束之后才知道,接着两个人轮流从袋子里面取球,球的颜色只有取出来之后才知道,每次取球数量在\(1\sim 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(\sum\limits_{j=0}^{i}\frac{\binom r j \binom b {i-j}}{\binom {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\n",f[R][B]);
}
posted @ 2018-10-28 11:32  yoyoball  阅读(528)  评论(0编辑  收藏  举报