BZOJ 3150 [Ctsc2013]猴子 概率&期望+高斯消元

概率&期望

首先遇到这到题我们可以想出来一个DP的方法,设 \(f[S]\) 为当前小Q手里的牌的状态为 \(S\) 时,小Q能赢的概率.

我们枚举 \(S\) 能转移的其他状态,就能列出来转移方程,一共有 \(2^n\) 个方程.

解方程我们需要用到高斯消元,所以这样做时间复杂度为 \(O((2^n)^3)\) .

这样做肯定爆炸,所以我们需要用到一个结论:

\(A,B\) 为两种牌的状态,若 \(A∩B=∅\),则 \(f[A∪B]=f[A]+f[B]\)

证明:

我们可以想象现在有3个人 \(a,b,c\) 在玩这个游戏,他们手里的牌分别是 \(A,B,C\) ,每次随机选两个人进行题目中的描述的步骤游戏.

显然此时有 \(f[A]+f[B]+f[C]=1\) ,也就是 \(f[A]=1-(f[B]+f[C])\) .

对于 \(a\) 而言,\(b,c\) 可以看成一个整体(因为三人游戏时b和c之间游戏时无论谁赢了牌,都相当于二人游戏时b的牌没有变动),此时 \(f[A]=1-f[B∪C]\) .

故上述结论成立。

所以我们设 \(f[i]\) 表示手上只有第 \(i\) 张牌时的胜率,对于每次询问我们只需要将小Q手上有的牌的 \(f\) 加起来即可。

求f我们也可以用DP的方法

\(\displaystyle f[i]=\sum_{j!=i}\frac{P[i][j] \times (f[i]+f[j])}{n-1}\)

所以方程就是

\(\displaystyle ((n-1) - \sum_{j!=i}P[i][j])\times f[i]=\sum_{j!=i}P[i][j] \times f[j]\)

高斯消元就能解决

注意最后一个方程应该是\(\displaystyle \sum_{i=1}^nf[i]=1\)

#include <iostream>
#include <cstdio>
using namespace std;
int n, m;
char s[105];
double tmp, a[105][105];
double Abs(double x){return x > 0 ? x : -x;}
void Gauss()
{
	for(int i = 1, j, k;i <= n; ++ i)
	{
		for(k = i, j = i + 1;j <= n;++ j)
			if(Abs(a[j][i]) > Abs(a[k][i]))k = j;
		swap(a[k], a[i]);
		for(j = i + 1;j <= n; ++ j)
			for(tmp = a[j][i] / a[i][i],k = i;k <= n + 1;++ k)a[j][k] -= a[i][k] * tmp;
	}
	for(int i = n; i >= 1; -- i)
	{
		for(int j = i + 1; j <= n;++ j)a[i][n+1] -= a[i][j] * a[j][n + 1];
		a[i][n+1] /= a[i][i];
	}
}
int main()
{
	cin >> n >> m;
	for(int i = 1; i < n; ++ i)
	{
		a[i][i] = 1 - n;
		for(int j = 1; j <= n; ++ j)
		{
			scanf("%lf", &tmp);
			if(i != j)a[i][j] = tmp,a[i][i] += tmp;
		}
	}
	for(int i = 1; i <= n; ++ i)scanf("%lf", &tmp),a[n][i] = 1;
	a[n][n + 1] = 1;
	Gauss();
	while(m--)
	{
		scanf("%s", s+1);tmp = 0;
		for(int i = 1; i <= n; ++ i)
			if(s[i] == '1')tmp += a[i][n + 1];
		printf("%0.8f\n", tmp);
	}
	return 0;
}
posted @ 2021-07-16 10:15  wljss  阅读(97)  评论(0编辑  收藏  举报