Luogu 2059 [JLOI2013]卡牌遊戲(概率dp)

傳送門

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

題解:由於正向推導(從人多倒人少)情況太多(無法記錄每種狀態的莊家編號),考慮倒着算(從人少到人多)。定義f[i][j]表示i個人的局,第j個人贏的概率。初始狀態爲f[1][1]=1,i從2算到n之後,直接規定1號是莊家,那麼f[n][1:n]這n個數就是答案。

對於每個狀態(i, j),枚舉當前抽到第k牌,那麼被淘汰的人相對當前莊家的編號可以由k和i算出,設爲t,那麼轉移方程如下:

如果t>j,那麼f[i][j]+=f[i-1][j+i-t]/m

如果t<j,那麼f[i][j]+=f[i-1][j-t]/m

如果t==j,那麼第j個人就被淘汰了,此時不對贏的概率造成貢獻

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int N=54;
double f[N][N];//in the circle of i players, the probability the victory of No.j player.
int n,m,a[N];
int main() {
	scanf("%d%d",&n,&m);
	for (int i=1;i<=m;++i)
		scanf("%d",&a[i]);
	f[1][1]=1.0;
	for (int i=2;i<=n;++i)
		for (int j=1;j<=i;++j)
			for (int k=1;k<=m;++k) {
				int t=(!a[k]%i)?i:a[k]%i;
				if (t>j) f[i][j]+=f[i-1][j+i-t]/m;
				else if (t<j) f[i][j]+=f[i-1][j-t]/m;
				//else No.j player loses
			}
	for (int i=1;i<=n;++i) {
		printf("%.2lf%% ",f[n][i]*100); 
	} 
	return 0;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章