P5415 [YNOI2019] 游戏 题解

题目传送门

思路

首先,这道题是一道显然的概率题。直接上 DP。

我们称当前的赢家为擂主。设 \(dp_{i, j}\) 代表第 \(k\) 个人在 \(i\) 号位置,擂主已经连赢 \(j\) 场时,第 \(k\) 个人获胜的概率。那我们以第 \(k\) 个人的位置分类讨论。

1. 当 \(k\) 为擂主时:

此时 \(k\) 所在的位置为 \(1\)。那 \(k\) 就有 \(\dfrac{1}{4}\) 的概率再赢 \(1\) 局,有 \(\dfrac{3}{4}\) 输掉比赛。若输掉比赛,\(k\) 应排到第 \(n - 2\) 位置。

故可以得到状态转移方程:

\[dp_{1,j}=dp_{1,j+1}\times \dfrac{1}{4}+dp_{n-2,1}\times \dfrac{3}{4} \]

2. 当 \(k\) 在比赛中,但不是擂主时:

此时需要分别讨论 \(k\)\(2\)\(3\)\(4\) 位置的情况,还需要进一步讨论是谁赢了这几种情况。这十分复杂,留给读者自己推导。最后可得下述几个状态转移方程:

\[dp_{2,j}=dp_{1,1}\times \dfrac{1}{4}+dp_{n-2,j+1}\times \dfrac{1}{4}+dp_{n-1,1}\times \dfrac{1}{2}\\ dp_{3,j}=dp_{1,1}\times \dfrac{1}{4}+dp_{n-1,j+1}\times \dfrac{1}{4}+dp_{n,1}\times \dfrac{1}{4}+dp_{n-1,1}\times \dfrac{1}{4}\\ dp_{4,j}=dp_{1,1}\times \dfrac{1}{4}+dp_{n,1}\times \dfrac{1}{2}+dp_{n-2,j+1}\times \dfrac{1}{4} \]

3. 当 \(k\) 在队伍中时:

此时只与当前擂主的输赢即可。易得下述状态转移方程:

\[dp_{i,j}=dp_{i-3,j+1}\times \dfrac{1}{4}+dp_{i-3,1}\times \dfrac{3}{4} \]

其中 \(5 \le i \le n\)

最后答案就是 \(dp_{k,0}\)

但你会发现,DP 无法转移,因为有互相的依赖性。可因为 \(n \le 10\)\(m \le 10\),所以可以直接跑高斯消元。

最后时间复杂度为 \(O(n^3m^3)\),可以通过本题。

代码

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

const int N = 155;
const double eps = 1e-12;

int t, n, m, k;
double a[N][N];

int calc(int x, int y)
{
	return (x - 1) * (m + 1) + y;
}

void gauss(int n)
{
	for (int i = 0; i < n; i++)
	{
		int id = i;
		for (int j = i + 1; j < n; j++)
			if (fabs(a[j][i]) > fabs(a[id][i])) id = j;
		if (fabs(a[id][i]) < eps) continue;
		swap(a[i], a[id]);
		double div = a[i][i];
		for (int j = 0; j <= n; j++)
			a[i][j] /= div;
		for (int j = i + 1; j < n; j++)
		{
			double mul = a[j][i];
			for (int k = i; k <= n; k++)
				a[j][k] -= a[i][k] * mul;
		}
	}
	for (int i = n - 1; i >= 0; i--)
	{
		for (int j = i - 1; j >= 0; j--)
		{
			a[j][n] -= a[j][i] * a[i][n];
			a[j][i] = 0;
		}
	}
	return;
}

void solve()
{
	memset(a, 0, sizeof(a));
	scanf("%d%d%d", &n, &m, &k);
	int cur = 0, maxp = n * (m + 1);
	a[cur][calc(1, m)] += 1.0, a[cur][maxp] += 1.0;
	cur++;
	for (int i = 2; i <= n; i++)
	{
		a[cur][calc(i, m)] += 1.0;
		cur++;
	}
	for (int i = 0; i < m; i++)
	{
		a[cur][calc(1, i)] += 1.0;
		a[cur][calc(1, i + 1)] -= 0.25;
		a[cur][calc(n - 2, 1)] -= 0.75;
		cur++;
	}
	for (int i = 0; i < m; i++)
	{
		a[cur][calc(2, i)] += 1.0;
		a[cur][calc(1, 1)] -= 0.25;
		a[cur][calc(n - 2, i + 1)] -= 0.25;
		a[cur][calc(n - 1, 1)] -= 0.5;
		cur++;
	}
	for (int i = 0; i < m; i++)
	{
		a[cur][calc(3, i)] += 1.0;
		a[cur][calc(1, 1)] -= 0.25;
		a[cur][calc(n - 1, 1)] -= 0.25;
		a[cur][calc(n - 1, i + 1)] -= 0.25;
		a[cur][calc(n, 1)] -= 0.25;
		cur++;
	}
	for (int i = 0; i < m; i++)
	{
		a[cur][calc(4, i)] += 1.0;
		a[cur][calc(1, 1)] -= 0.25;
		a[cur][calc(n, i + 1)] -= 0.25;
		a[cur][calc(n, 1)] -= 0.5;
		cur++;
	}
	for (int i = 5; i <= n; i++)
	{
		for (int j = 0; j < m; j++)
		{
			a[cur][calc(i, j)] += 1.0;
			a[cur][calc(i - 3, j + 1)] -= 0.25;
			a[cur][calc(i - 3, 1)] -= 0.75;
			cur++;
		}
	}
	gauss(maxp);
	double ans = a[calc(k, 0)][maxp];
	printf("%.6lf\n", ans);
	return;
}

int main()
{
	scanf("%d", &t);
	while (t--) solve();
	return 0;
}
posted @ 2025-07-19 22:03  lucasincyber  阅读(7)  评论(0)    收藏  举报