題意: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;
}