状态定义:
由于每一次会处决一个人,只剩下一个人,那么最后剩下的那个人一定是胜率 100%的,所以剩下两个人的胜率可以由剩下一个人的胜率转移过来 。状态定义不是显然,不如我们先看一下如果将状态定义为:
dp [ i ] = dp [ i - 1 ] + somework;
其中,dp [ i ] 的意义是剩下 i 个人的胜率 …… 欸,到底是谁的胜率呢?看来不是很好转移 ……
不如加一维,重新定义 dp [ i ]:
dp [ i ] [ j ] = dp [ i - 1 ] [ j ] + somework;
其中 dp [ i ] [ j ] 的意义是剩下 i 个人中的从庄家开始数的第 j 个人的胜率,看样子还行 。
尝试转移:
如何转移方程呢?
首先我们会枚举 j,也就是这次从庄家数第几个玩家,如果我们要把方程转移的话,必须知道这个人在上一个状态(剩下 i - 1 个人的状态)所在的位置 。
假设这次抽到了牌号为 X 的牌,那么显然它所决定要处决的人的位置在 ( a [ k ] % i ) 的位置上;这里有一个小细节,有可能会 % 成 0,所以当 % 成 0 的时候说明即将处决第 i 个人,所以直接特判一下就好了 。
但是处决的位置有两种:
令 Y = ( a [ k ] % i );
如果 Y > j:
发现这一状态(i)的第 j 个人是上一个状态的第 (i - Y + j)个人;
如果 Y > j:
发现这一状态(i)的第 j 个人是上一个状态的第 (j - Y)个人;
(上图和下图的顺序反了一下,不过不影响)
确定了位置,那么这次的第 j 个人的胜率就可以由上一个 j' 推过来,当然,这只是一种抽牌方式,所以要乘以概率 1/m,然而这也不只是唯一的一种获胜(避开处决)的方式(因为抽的牌可能不同),而且多种方式并列产生胜率,所以,加法原理 ——
得到 Dp 方程:
Y > j: dp [ i ] [ j ] += dp [ i - 1 ] [ i - c + j ] / m ;
Y < j: dp [ i ] [ j ] += dp [ i - 1 ] [ j - c ] / m ;
代码:
#include <bits/stdc++.h>
const int N = 50 + 1 ;
//using namespace std ;
double dp [ N ] [ N ] ;
int n , m , a [ N ] ;
int main ( ) {
scanf ( "%d%d" , & n , & m ) ;
for ( int i = 1 ; i <= m ; i ++ )
scanf ( "%d" , & a [ i ] ) ;
dp [ 1 ] [ 1 ] = 1 ;
for ( int i = 2 ; i <= n ; i ++ ) {
for ( int j = 1 ; j <= i ; j ++ )
for ( int k = 1 ; k <= m ; k ++ ) {
int c = ( a [ k ] % i ) == 0 ? i : a [ k ] % i ;
if ( c > j ) {
dp [ i ] [ j ] += dp [ i - 1 ] [ i - c + j ] / m ;
}
else {
dp [ i ] [ j ] += dp [ i - 1 ] [ j - c ] / m ;
}
}
}
for ( int i = 1 ; i <= n ; i ++ )
printf ( "%.2lf" , 100 * dp [ n ] [ i ] ) , printf ( "%% " ) ;
return 0 ;
}
坚持取消后摇!