1. 程式人生 > >Luogu2059 卡牌遊戲-概率DP

Luogu2059 卡牌遊戲-概率DP

define pri 得到 同時 bsp += for html class

蒟蒻的第一道概率DP。。

先講一下我最開始yy的一個算法吧:

我們設f[i][j]表示當前進行了i輪,第j個人坐莊的概率是多少。

為什麽這麽想呢,因為進行了到第n輪後最後一個人必然是莊,同時這就是它的獲勝的概率是吧。

可以發現轉移也是很好想的。f[i][j]=f[i-1][last]/m 。 但是

嗯嗯,last怎麽求,好像求不出啊。。所以就掛了??唉。。

但是這也是很有啟發性的,

我們發現這樣搞不出的原因是:它的淘汰的是圍繞著莊轉一定步數後的數。

而我們無法維護哪些已經被淘汰,所以我們無法繼續下去。

所以我們考慮怎樣不需要維護也能得到答案,那麽考慮到逆推,同時把這個c數組(牌堆)更好的運用上。

我們考慮還剩下i個人,從莊數其第j個人獲勝的概率是多少。

那麽我們可以發現這樣做可以保證,每一個狀態都能根據c從一個確定的地方轉移過來。

仔細來講:

我們可以首先枚舉莊家抽到的卡牌k,得到這一輪被淘汰的人的位置c

如果c=j ,就不要考慮了(因為這表示此輪第j個人被淘汰)。

而第c個人被淘汰之後,剩下的i-1個人要組成一個新的環,莊家為第c個人的下一個。

c>j時,第j個人是新的環裏從新莊家數起的第i?c+j個人,

c<j時,第j個人是新的環裏從新莊家數起的第j?c個人。

這樣就基本完成了這個題目。

聽某些巨lao講一般的概率DP都是逆推,反正我是先這麽受教了,有大佬願意提出自己的看法,歡迎評論,謝謝

#include <cstdio>
using namespace std;

#define RG register

int n,m,c[66];
double f[66][66];

int main()
{
    RG int i,j,k,Ne;
    scanf ("%d%d", &n, &m);
    for (i=1;i<=m;++i) scanf ("%d", &c[i]);
    f[1][1]=1.0;
    for (i=2;i<=n;++i)
        for (j=1;j<=i;++j)
            
for (k=1;k<=m;++k) { Ne=c[k]%i; if (!Ne) Ne=i; if (Ne<j) f[i][j]+=f[i-1][j-Ne]/m; if (Ne>j) f[i][j]+=f[i-1][i-Ne+j]/m; } for (i=1;i<=n;++i) printf ("%.2lf%% ", f[n][i]*100.0); return 0; }

Luogu2059 卡牌遊戲-概率DP