蒟蒻的第一道概率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;
}