TopCoder - 12349 SRM579 Round1 Div1 RockPaperScissors (概率dp)

题目大意:

有$n$个骰子,每个骰子有300个面,其中有$a_i,b_i,c_i$分别为石头/布/剪刀

每轮你选择出石头/剪刀/布,然后会从剩下的骰子中随机取一个再随机结果,但是你不知道取的是什么骰子

赢一局的权值为3,平局为1

求最优情况下最大的权值期望

题目分析:

显然当前的局面只和已经抽出的石头/布/剪刀的数量有关(因为这是你唯一的决策依据,也是唯一影响局面的)

乍一看非常抽象的决策过程,实在无法通过分析得到每一种局面下应该作出的决策

于是考虑能否直接把每一种局面出现的概率求出,决策后将期望线性相加

不妨把随机取出的骰子放在一个序列上,一个局面是由已经出现的骰子和当前第$i$次决策的骰子组成的

令$dp_{i,j,k,typ}$表示已经出现的骰子中出现$i,j,k$个石头/布/剪刀,当前决策时骰子结果为$typ$的概率

实际在$dp$时,$typ$一维需要额外加入一个值表示未确定下一个是什么

考虑对于每个骰子,枚举它是已经出现/正在决策/在第$i$次决策之后出现,将其概率累和

$dp$状态为$O(n^3)$,转移次数为$O(n)$,复杂度为$O(n^4)$

最后对于每种局面计算最优的决策即可

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
#include<bits/stdc++.h>
using namespace std;

#define rep(i,a,b) for(int i=a,i##end=b;i<=i##end;++i)
#define drep(i,a,b) for(int i=a,i##end=b;i>=i##end;--i)
template <class T> inline void cmin(T &a,T b){ ((a>b)&&(a=b)); }
template <class T> inline void cmax(T &a,T b){ ((a<b)&&(a=b)); }

class RockPaperScissors {
private:
static const int N=53;
static const int eps=1e-9;
double F[N][N][N][4],G[N][N][N][4],w[3];
double C[N][N];

public:

double bestScore(vector <int> w1, vector <int> w2, vector <int> w3) {
int n=w1.size();
rep(i,0,n) rep(j,C[i][0]=1,i) C[i][j]=C[i-1][j-1]+C[i-1][j];
memset(F,0,sizeof F),F[0][0][0][3]=1;
rep(i,1,n) {
w[0]=w1[i-1]/300.0; w[1]=w2[i-1]/300.0; w[2]=w3[i-1]/300.0;
rep(a,0,i) rep(b,0,i-a) rep(c,0,i-a-b) rep(d,0,3) G[a][b][c][d]=F[a][b][c][d];
rep(a,0,i) rep(b,0,i-a) rep(c,0,i-a-b) rep(d,0,3) if(G[a][b][c][d]>eps) {
// 枚举这个骰子在之前出现过了
F[a+1][b][c][d]+=G[a][b][c][d]*w[0];
F[a][b+1][c][d]+=G[a][b][c][d]*w[1];
F[a][b][c+1][d]+=G[a][b][c][d]*w[2];
// 枚举这个骰子为下一个出现的
if(d==3) rep(e,0,2) F[a][b][c][e]+=G[a][b][c][d]*w[e];
}
}
double ans=0;
rep(a,0,n-1) rep(b,0,n-1-a) rep(c,0,n-1-a-b) {
// 决策这一轮出什么
double ma=0;
rep(d,0,2) cmax(ma,F[a][b][c][d]+F[a][b][c][(d+2)%3]*3);
ans+=ma/C[n][a+b+c]/(n-a-b-c);
}
return ans;
}
};