[CF148D]Bag of mice[题解]

Bag of mice

  • 前言:今天听 \(w\) 老师讲课,思考此题时我给出了和题解不同的做法,但是不知道正确性。下课后花费一个多小时,按照这种做法过掉了这道题目,写一篇题解总结。

题意

给定 \(w\) 只白色老鼠,\(b\) 只黑色老鼠,\(A\)\(B\) 两个人轮流抓取(不放回),先抓到白色老鼠的人赢。

\(B\) 每次抓取老鼠后,会从剩下的老鼠随机 跑掉一只。

\(A\) 先手获胜的概率。

\(w,b\leq 1000\)

分析

不难发现此题有一个简单性质:如果当前轮次游戏没有结束,则 \(A\)\(B\) 此前抓到的都是黑色老鼠。

考虑枚举游戏进行到第 \(i\) 轮,我们很容易知道之前一共被抓走了 \(i - 1\) 只黑色老鼠,问题的关键在于 \(B\) 选取后随机跑掉的老鼠,如果我们知道这里面跑掉了多少只白色老鼠,很显然我们可以由此推测出当前轮次所有的状态。

所以,在第 \(i\) 轮枚举剩下 \(j\) 只白色老鼠,由于游戏没有结束,则跑掉了 \(w - j\) 只白色老鼠,而一共跑掉了 \((i - 1) / 2\) 只老鼠,所以跑掉了 \((i - 1) / 2 - (w - j)\) 只黑色老鼠。

\(x\) 表示第 \(i\) 轮剩余的白色老鼠数量,\(y\) 表示第 \(i\) 轮剩余的黑色老鼠数量,显然,我们有:

\[x = j,y = b - (i - 1) - [(i - 1) / 2 - (w -j)] \]

考虑如何转移状态。

\(f[0][i]\) 表示上一轮次剩余 \(i\) 只白色老鼠的概率,则当在 \(A\) 行动轮次时,我们只需要枚举白色老鼠的数量 \(j\),就可以有:

\[ans = ans + f[0][j]\times \frac{x}{x + y} \]

接下来我们只需要维护 \(f[1][i]\) 即当前轮次结束过后剩余 \(i\) 只白色老鼠的概率。

若为 \(A\) 操作轮次,则我们只需要在每一种概率上乘上选择黑色老鼠的概率 \(\frac{y}{x + y}\)
即可。

若为 \(B\) 操作轮次,则枚举白色老鼠数量 \(j\),显然,如果跑掉了白色老鼠,则有:

\[f[1][j - 1] = f[0][j] \times \frac{y}{x +y}\times \frac{x}{x + y - 1} \]

即上一轮剩下 \(j\) 只白老鼠的概率乘上 \(B\) 抓取到黑色老鼠的概率再乘上剩余老鼠中放掉白老鼠的概率。

同理,如果跑掉了黑色老鼠,则有

\[f[1][j - 1] = f[0][j] \times \frac{y}{x +y}\times \frac{y - 1}{x + y - 1} \]

最后只需要注意一些细节和边界即可。

\(code\)

#include <bits/stdc++.h>
using namespace std;
const int N = 1e3 + 10;
inline int read()
{
	int s = 0, w = 1;
	char ch = getchar();
	while(ch < '0' || ch > '9') { if(ch == '-') w *= -1; ch = getchar(); }
	while(ch >= '0' && ch <= '9') s = s * 10 + ch - '0', ch = getchar();
	return s * w;
}
int w, b, opt;
double f[2][N]; //f[i] 表示还剩下 i 只白鼠的概率 
double ans;
int main()
{
	w = read(), b = read();
	f[1][w] = 1; //初始有 w 只白鼠 
	for(register int i = 1; ; i++, opt ^= 1){ //枚举游戏轮次 
//		cout << "轮次: " << i << "\n";
//		cout << "上一轮剩下 j 只白鼠的概率:\n"; 
//		for(register int j = 1; j <= w; j++)
//			cout << f[opt ^ 1][j] << "\n"; 
		bool flag = false;
		for(register int j = 1; j <= w; j++)
			if(f[opt ^ 1][j]) flag = true;
		if(!flag) break;
		if(i & 1){
			//A 获胜的概率即选到白鼠的概率
			for(register int j = 1; j <= w; j++){ //枚举剩余白鼠的数量 
				int x = j, y = b - (i - 1) - ((i - 1) / 2 - (w - j));
				if(y < 0) continue;
				ans = ans + f[opt ^ 1][j] * ((double)x / (double)(x + y));
			}
		}
		if(!(i & 1)){
			for(register int j = 1; j <= w; j++){ //枚举剩余白鼠的数量 
				int x = j, y = b - (i - 1) - ((i - 1) / 2 - (w - j));
				if(y < 1) continue;
				//计算本轮还没有进行前,剩余白鼠为 j,则剩余黑鼠的数量 
				/*剩余 j 只白鼠时,由于进行了 i - 1 轮游戏,被抓走了 i - 1 只,且前 i - 1 轮游戏
				B 放跑了 (i - 1) / 2 下取整只老鼠,由于走掉的白鼠即 w - j 一定是被放跑的,所以放跑
				的黑鼠数量为 i / 2 - (w - j)。
				*/
				//本轮放跑了一只白鼠
			//	cout << j << " " << x << " " << y << "\n"; 
				f[opt][j - 1] += f[opt ^ 1][j] * ((double)y / (double)(x + y)) * ((double)x / (double)(x + y - 1)); //枚举本轮放跑的白鼠的数量 
				//cout << f[opt ^ 1][j] << "\n";
				//cout << y << " " << x + y << " " << y - 1 << " " << x + y - 1 << "\n";
				f[opt][j] += f[opt ^ 1][j] * ((double)y / (double)(x + y)) * ((double)(y - 1) / (double)(x + y - 1));
				//cout << f[opt][j] << "\n";
				//乘上被抓黑色老鼠的概率
			}
		}
		else{ //不会放跑老鼠 
			for(register int j = 1; j <= w; j++){
				int x = j, y = b - (i - 1) - ((i - 1) / 2 - (w - j));
				if(y < 0) continue;
				f[opt][j] = f[opt ^ 1][j] * ((double)y / (double)(x + y)), f[opt ^ 1][j] = 0;
			}
		}
	}
	printf("%.10lf\n", ans);
	return 0;
}
posted @ 2022-05-12 21:27  ╰⋛⋋⊱๑落叶๑⊰⋌⋚╯  阅读(28)  评论(0编辑  收藏  举报