[BZOJ 3191][JLOI 2013]卡牌游戏

时间:2023-01-31 17:14:59

觉得这题很有必要讲一下!

现在发现在做概率题,基本是向 dp 和 马尔可夫链 靠齐

但是这一题真是把我坑了,因为状态太多,马式链什么的直接死了

我一开始的想法就是用 f[i][j] 表示剩余 i 个人,现由 j 坐庄

恩~ dp 方程还是老好想的嘛~

wait! wait!! wait!!! 这尼马有后效性啊!有后效性啊!!

然后想了半天毫无进展,一看题解……

哎呦我 c

其实那个方程几乎对了,但!是!有后效性就要用重标号的方法去掉!

怎么算去了呢?

其实……谁是谁更本不重要!

如果我们对每个人 i 都重标号为 0 ,列一次方程,那么每次都只算第 0 个人 (变为 0~n-1 个人) 存活的概率 即 f[1][0] 即可

所有方程的区别只在边界条件不同而已,标号什么的完全不用管,也就没有后效性了!

然后我们只要保证 0 不 over 即可,其他的都可以不用管了

若这一轮的标号是 0~i , 然后第 j 个人 over 的话

新标号为 0~i-1, 但是这里的 j~i-1 其实是原来的 j+1~i

但是我们不用去管标号变了的问题,因为本质上是一样的

f[i][(j+a[k]-1)%(i+1)]+=f[i+1][j]/M

这么写就可以了,但是还有个问题:

0 是不能被删的我们注意到在 f[i+1] -> f[i] 时

f[i][0] 其实是 0 被删去后 1 变为 0 坐庄的概率

但 0 存活到了最后,这是无法取的(事实上若 0 活到了最后,1 就不可能坐庄)

那么 f[i][0] 其实是什么呢?

f[i][i]!

因为这是一个环,在剩余 i+1 个人时,删掉了第 i 个人,在坐庄时当然由第 0 人来,这时所有人都没有被重标号

 #include <cstdio>
#include <cstring>
const int sizeOfPlayer=;
const int sizeOfCard=; inline int getint(); int N, M;
int a[sizeOfCard];
double f[sizeOfPlayer][sizeOfCard]; int main()
{
N=getint(), M=getint();
for (int i=;i<M;i++)
a[i]=getint(); for (int p=;p<N;p++)
{
memset(f, , sizeof(f));
f[N][(N-p)%N]=1.0;
for (int i=N-;i;i--)
{
for (int j=;j<=i;j++)
for (int k=;k<M;k++)
f[i][(j+a[k]-)%(i+)]+=f[i+][j]/M;
f[i][]=f[i][i];
} if (p) putchar(' ');
printf("%.2lf%%", f[][]*);
} putchar('\n'); return ;
} inline int getint()
{
register int num=;
register char ch;
do ch=getchar(); while (ch<'' || ch>'');
do num=num*+ch-'', ch=getchar(); while (ch>='' && ch<='');
return num;
}

说的不清楚还请见谅