[HOJ] 最高分

题目大意

\(n\) 人参加 \(m\) 轮比赛,第 \(i\) 个人在第 \(j\) 场比赛拿到 \(k\) 分的概率是 \(a_{i,j,k}\),每一场比赛有且只有一人能够拿分,求 \(m\) 场比赛结束后选手中最高分的期望是多少。

保证 \(\forall i\in[1,m],\sum\limits_{j=1}^n\sum\limits_{k=1}^3 a_{j,i,k}=1\)

\(1\le n\le 20,1\le m\le 10,1\le k\le 3\)

解题思路

看到如此小的 \(m\),可以想到状压dp。

因为最终答案要求的是最大值的期望,所以我们可以考虑用一维存储最大值,\(f\) 的值表示到达该状态的概率,求期望时将两项相乘求和即可。

但是如果将每个人 "分配" 给每场比赛,会发现并不好转移,所以我们考虑反过来,枚举每个人,再定赢了哪些比赛。

所以我们可以想到令 \(f_{i,j,k,w}\) 表示枚举到第 \(i\) 个人,第 \(i\) 个人得了 \(j\) 分,前 \(i\) 个人中最高分为 \(k\)\(w\) 这些比赛的胜者已经确定的概率。

所以得到了转移方程:

\[f_{i,j,k,w}=\begin{cases}\sum\limits_{l\in w}\sum\limits_{t=1}^3f_{i,j-t,k,w-l}\ a_{i,l,t}&k> j\\ \sum\limits_{l\in w}\sum\limits_{t=1}^3(f_{i,j-t,j-t,w-l}+f_{i,j-t,k,w-l})\ a_{i,l,t}&k=j\\ \end{cases} \]

第二行表示第 \(i\) 个人是新的最高分的概率。

特别地,有:

\[f_{i,0,k,w}=\sum\limits_{j=0}^k f_{i-1,j,k,w} \]

时间复杂度为 \(O(nm^32^m)\)

细节

注意多层循环的顺序,否则会导致重复统计。

代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;

#define int ll
const int N = 22, M = 11, p = 1e9 + 7;
int f[N][M * 3][M * 3][1 << M], a[M][N][3], n, m;

ll qpow(ll a, ll b)
{
	if(b == 1) return a;
	return (b & 1) ? a * qpow(a * a % p, b >> 1 ) % p : qpow(a * a % p, b >> 1);
}

int inv = qpow(1e6, p - 2);

signed main()
{
	ios::sync_with_stdio(0);cin.tie(0);
	cin >> n >> m;
	
	for(int w = 1; w <= 3; w ++)
	for(int i = 1; i <= m; i ++)
	for(int j = 1; j <= n; j ++)
	{
		cin >> a[i][j][w];
		a[i][j][w] = a[i][j][w] * inv % p;
	}

	f[0][0][0][0] = 1;
	int _2m = 1 << m, m3 = m * 3;
	for(int i = 1; i <= n; i ++)
	{
		int ss = 0;
		for(int j = 0; j <= m3; j ++)
		for(int k = j; k <= m3; k ++)
		for(int w = 0; w < _2m; w ++)
			f[i][0][k][w] = (f[i][0][k][w] + f[i - 1][j][k][w]) % p;
		for(int l = 0; l < m; l ++)
		for(int w = 0; w < _2m; w ++)
		{
			if(!(w & (1 << l))) continue;
			int nw = w ^ (1 << l);
			for(int j = m3; j >= 0; j --)
			{
				int &ff = f[i][j][j][w];
				for(int k1 = max(j - 3, 0ll); k1 < j; k1 ++)
				{
					if(j - 1 >= 0 && j - 1 <= k1) ff = (ff + f[i][j - 1][k1][nw] * a[l + 1][i][1]) % p;
					if(j - 2 >= 0 && j - 2 <= k1) ff = (ff + f[i][j - 2][k1][nw] * a[l + 1][i][2]) % p;
					if(j - 3 >= 0 && j - 3 <= k1) ff = (ff + f[i][j - 3][k1][nw] * a[l + 1][i][3]) % p;
				}
				for(int k = j; k <= m3; k ++)
				{
					int &now = f[i][j][k][w];
					if(j - 1 >= 0) now = (now + f[i][j - 1][k][nw] * a[l + 1][i][1]) % p;
					if(j - 2 >= 0) now = (now + f[i][j - 2][k][nw] * a[l + 1][i][2]) % p;
					if(j - 3 >= 0) now = (now + f[i][j - 3][k][nw] * a[l + 1][i][3]) % p;
				}
			}
		}
	}
	int ans = 0, _end = _2m - 1, ss = 0;
	for(int i = 0; i <= m3; i ++)
		for(int j = i; j <= m3; j ++)
		{
			ans = (ans + j * f[n][i][j][_end]) % p;
			ss = (ss + f[n][i][j][_end]) % p;
		}
	cout << ans;

	return 0;
}
posted @ 2023-08-20 11:32  adam01  阅读(32)  评论(0)    收藏  举报