Luogu2059 卡牌游戏-概率DP

蒟蒻的第一道概率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个人是新的环里从新庄家数起的第ic+j个人,

c<j时,第j个人是新的环里从新庄家数起的第jc个人。

这样就基本完成了这个题目。

听某些巨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;
}

猜你喜欢

转载自www.cnblogs.com/Bhllx/p/9839396.html