1. 程式人生 > 實用技巧 >【洛谷P2059】卡牌遊戲

【洛谷P2059】卡牌遊戲

題目

題目連結:https://www.luogu.com.cn/problem/P2059
N個人坐成一圈玩遊戲。一開始我們把所有玩家按順時針從1到N編號。首先第一回合是玩家1作為莊家。每個回合莊家都會隨機(即按相等的概率)從卡牌堆裡選擇一張卡片,假設卡片上的數字為X,則莊家首先把卡片上的數字向所有玩家展示,然後按順時針從莊家位置數第X個人將被處決即退出遊戲。然後卡片將會被放回卡牌堆裡並重新洗牌。被處決的人按順時針的下一個人將會作為下一輪的莊家。那麼經過N-1輪後最後只會剩下一個人,即為本次遊戲的勝者。現在你預先知道了總共有M張卡片,也知道每張卡片上的數字。現在你需要確定每個玩家勝出的概率。

這裡有一個簡單的例子:

例如一共有4個玩家,有四張卡片分別寫著3,4,5,6.

第一回合,莊家是玩家1,假設他選擇了一張寫著數字5的卡片。那麼按順時針數1,2,3,4,1,最後玩家1被踢出遊戲。

第二回合,莊家就是玩家1的下一個人,即玩家2.假設玩家2這次選擇了一張數字6,那麼2,3,4,2,3,4,玩家4被踢出遊戲。

第三回合,玩家2再一次成為莊家。如果這一次玩家2再次選了6,則玩家3被踢出遊戲,最後的勝者就是玩家2.

思路

先求出 \(g[i][j]\) 表示在有 \(i\) 個人且 \(1\) 號是莊家的時候,下一個處決的是 \(j\) 號的方案數。顯然 \(\frac{g[i][j]}{m}\) 就是下一個處決 \(j\)

的期望。
然後設 \(f[i][j]\) 表示在剩餘 \(i\) 個人且 \(1\) 號是莊家,最後第 \(j\) 個人獲勝的期望。那麼

\[f[i][j]=\sum^{i}_{k=1}([k\neq j]\times f[i-1][(j-k+n)\bmod n]\times\frac{g[i][k]}{m}) \]

記憶化搜尋即可。時間複雜度 \(O(n^3)\)

程式碼

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

const int N=310;
const double eps=1e-12;
int n,m,a[N],g[N][N];
double f[N][N];

double dfs(int n,int x)
{
	if (n==1) return 1.0;
	if (fabs(f[n][x]-0)>eps) return f[n][x];
	for (int i=1;i<=n;i++)
		if (i!=x) f[n][x]+=dfs(n-1,(x-i+n)%n)*g[n][i]/m;
	return f[n][x];
}

int main()
{
	scanf("%d%d",&n,&m);
	for (int i=1;i<=m;i++)
		scanf("%d",&a[i]);
	for (int i=2;i<=n;i++)
		for (int j=1;j<=m;j++)
			g[i][(a[j]-1)%i+1]++;
	for (int i=1;i<=n;i++)
		printf("%.2lf%c ",dfs(n,i)*100,37);
	return 0;
}