抽卡指北

科学抽卡攻略

​ 众所周知,在魔法纪录的池子中,单抽十次和十连一次的概率并不相同,那么,是否在确定的单抽与十连数量下,一定存在一种最优策略,使得小 qb 获得 up 魔法少女的最高孔数的概率尽可能大呢?如果存在,最优策略与随机(或者最劣)策略的概率又相差多少呢?

​ 首先我们了解一下单抽与十连的相关机制,按照wiki上给的数据,有

通常 魔法少女确定时 三星以上确定时 四星魔法少女确定时
魔法少女 44 1%1\% 1%1\% 2%2\% 100%100\%

然后对于 up 池,这个四星有 60%60\% 的概率为 up 四星。

​ 对单抽来说,概率肯定为“通常”的 1%1\%,但是对于 1010 连来说,有一张是按照“魔法少女确定时”的概率确定,一张按照“三星以上确定时”的概率确定,其余 88 张按照通常的概率确定。

​ 这里需要注意一下这两张特殊卡的顺序,以及与保底重复的可能。

​ 对十连抽卡,首先生成 88 个通常 22 个特殊,然后打乱顺序作为可能的十连抽卡结果,接着判定当前是否超过 100/100100/100 的保底,如果在应该的保底位置及此位置之前没有出现四星,则将原本保底位置的卡删去,替换成“四星魔法少女确定时”。

​ 了解抽卡机制后,以下采用动态规划进行相关概率的计算。

​ 令 dpi,j,k,ldp_{i,j,k,l} 表示采用最佳抽卡策略(即最大化 dpi,j,k,ldp_{i,j,k,l})抽卡,还剩 ii 张十连券,jj 张单抽券,保底状态为 k/100k/100 的情况下,把这些单抽和十连按照一定顺序用完后抽到卡池 up 四星角色不小于 ll 次的概率

​ 初始状态:i,j,k\forall i,j,k ,有 dpi,j,k,0=1dp_{i,j,k,0}=1

​ 在写下转移之前,我们先规定一些值

qn=1%(表示单抽出现四星的概率)qup=0.6(表示出四星时为 up 的概率)fi,j(表示不触发保底的十连出 i 个 up 四星且最后一个出的四星在第 j 位的概率,0i10,1j10,ij)ui,j,k(表示在第 k 张触发保底的十连出 i 个 up 四星且最后一个出的四星在第 j 位的概率,0i10,1j10,ij)q0=(1qn)9(12qn)0.895246902533968(表示不触发保底的十连不出现四星的概率)\begin{aligned} &q_n=1\%(\text{表示单抽出现四星的概率})\\ &q_{up}=0.6(\text{表示出四星时为 up 的概率})\\ &f_{i,j}(\text{表示不触发保底的十连出 i 个 up 四星且最后一个出的四星在第 j 位的概率},0\le i \le10,1\le j\le 10,i\le j) \\ &u_{i,j,k}(\text{表示在第 k 张触发保底的十连出 i 个 up 四星且最后一个出的四星在第 j 位的概率},0\le i \le10,1\le j\le 10,i\le j)\\ &q_0=(1-q_n)^9(1-2q_n)\approx0.895246902533968(\text{表示不触发保底的十连不出现四星的概率})\\ \end{aligned}

​ 转移:

​ 因为式子有点大,不妨一点一点的考虑转移

​ 对某个 dpi,j,k,ldp_{i,j,k,l} ,若

  1. 通过单抽到达下一个状态且不涉及保底(k99k\not=99),有

    抽不到四星:从 dpi,j1,k+1,ldp_{i,j-1,k+1,l} 处转移,概率为 (1qn)(1-q_n)

    抽到四星但不为 up 角色:从 dpi,j1,0,ldp_{i,j-1,0,l} 处转移,概率为 qn×(1qup)q_n\times(1-q_{up})

    抽到四星且为 up 角色:从 dpi,j1,0,l1dp_{i,j-1,0,l-1} 处转移,概率为 qn×qupq_n\times q_{up}

  2. 通过单抽到达下一个状态且涉及保底(k=99k=99),有

    为 up 角色:从 dpi,j1,0,ldp_{i,j-1,0,l} 处转移,概率为 1qup1-q_{up}

    不为 up 角色:从 dpi,j1,0,l1dp_{i,j-1,0,l-1} 处转移,概率为 qupq_{up}

  3. 通过十连到达下一个状态且不涉及保底(0k890\le k\le 89

    抽不到四星:从 dpi1,j,k+10,ldp_{i-1,j,k+10,l} 处转移,概率为 q0q_0

    抽到四星且抽到 nn (0n10)0\le n\le10) 个当期 upup 且最后一个出现的四星在位置 mm (nm10n\le m\le 10),对相应的 nnmm:从 dpi1,j,10m,max(0,ln)dp_{i-1,j,10-m,\max(0,l-n)} 处转移,概率为 fn,mf_{n,m}

  4. 通过十连到达下一个状态且涉及保底 (90k99)(90\le k\le 99)

    抽到四星且抽到 nn (0n10)0\le n\le10) 个当期 upup 且最后一个出现的四星在位置 mm (nm10n\le m\le 10),对相应的 nnmm:从 dpi1,j,10m,max(0,ln)dp_{i-1,j,10-m,\max(0,l-n)} 处转移,概率为 u100k,n,mu_{100-k,n,m}

dpi,j,k,l={max(dpi,j1,k+1,l×(1qn)+dpi,j1,0,l×qn(1qup)+dpi,j1,0,l1×qnqup,         dpi1,j,k+10,l×p0+n=0lm=max(1,n)10dpi1,j,10m,max(0,ln)×fn,m),0k89max(dpi,j1,k+1,l×(1qn)+dpi,j1,0,l×qn(1qup)+dpi,j1,0,l1×qnqup,         n=0lm=max(1,n)10dpi1,j,10m,max(0,ln)×u100k,n,m),90k98max(dpi,j1,0,l×(1qup)+dpi,j1,0,l1×qup,n=0lm=max(1,n)10dpi1,j,10m,max(0,ln)×u100k,n,m),k=99dp_{i,j,k,l}= \left\{\begin{aligned} &\max(dp_{i,j-1,k+1,l}\times(1-q_n)+dp_{i,j-1,0,l}\times q_n(1-q_{up})+dp_{i,j-1,0,l-1}\times q_nq_{up},\\&\ \ \ \ \ \ \ \ \ dp_{i-1,j,k+10,l}\times p_0+\sum_{n=0}^{l}\sum_{m=\max(1,n)}^{10}dp_{i-1,j,10-m,\max(0,l-n)}\times f_{n,m}),0\le k\le 89\\ &\max(dp_{i,j-1,k+1,l}\times(1-q_n)+dp_{i,j-1,0,l}\times q_n(1-q_{up})+dp_{i,j-1,0,l-1}\times q_nq_{up},\\&\ \ \ \ \ \ \ \ \ \sum_{n=0}^{l}\sum_{m=\max(1,n)}^{10}dp_{i-1,j,10-m,\max(0,l-n)}\times u_{100-k,n,m}),90\le k\le 98\\ &\max(dp_{i,j-1,0,l}\times (1-q_{up})+dp_{i,j-1,0,l-1}\times q_{up},\sum_{n=0}^{l}\sum_{m=\max(1,n)}^{10}dp_{i-1,j,10-m,\max(0,l-n)}\times u_{100-k,n,m}) ,k=99 \end{aligned}\right.

​ 下面考虑 ffuu 的计算

​ 本来想使用数学方法算一算的,但尝试后发现极度麻烦,还是用计算机暴力的方法计算比较方便。

​ 考虑枚举出某次 1010 连的全部结果,首先枚举“三星以上确定时”的位置和“四星魔法少女确定时”的位置, 然后枚举每个位置的魔法少女是非四星/四星非up/四星up三种情况的哪一种,最后确定是否需要替换“四星魔法少女确定时”位置为四星魔法少女。


​ 考虑究竟为什么抽卡顺序影响最终抽卡概率?

​ 其实,只有”四星魔法少女确定时“把”三星以上确定时“覆盖时,抽卡概率才会被影响。

​ 那么,是否存在简单的策略,比如:

​ “对保底状态 k/100,若 k<90 且小 qb 仍有十连,使用十连,否则单抽” (后称简单策略1)

​ “对保底状态 k/100,若 k<90 且小 qb 仍有十连,使用十连,否则选择 1.使用十连 2.使用单抽直到k=0中的期望较大者” (后称简单策略2)

​ 很遗憾的是,在蒙特卡洛模拟下,这两者并没有上面最暴力计算出方式优秀。


​ 这个坑暂时先这样,我也懒得管了,欢迎提出问题,下面放一下不保证正确性的相关代码。

暴力dp
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
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
#include <cstdio>
#include <cstdlib>
#include <algorithm>
double qn=0.01,qup=0.6,q0;
double u[11][11][11];//u[0]即f
int bit[11];
void calc(int p3,int p4)
{
double QN=qn;
double mp=1;
int cntup=0,las=0;
for(int i=1;i<=10;i++)
{
cntup+=!bit[i];
if(i==p3) QN=qn+0.01;
if(i==p4&&!las) QN=1;
if(bit[i]<2) las=i;
if(bit[i]==0) mp*=QN*qup;
else if(bit[i]==1) mp*=QN*(1-qup);
else mp*=1-QN;
if(i==p3||i==p4) QN=qn;
}
u[p4][cntup][las]+=mp/10.0;
}
void init_fu()
{
for(int s=0;s<59049;s++)
{
int sta=s;
//0 up 1 四非up 2 非四
for(int i=1;i<=10;i++) bit[i]=sta%3,sta/=3;
for(int i=1;i<=10;i++)//钦定“三星确定时”
{
for(int j=0;j<=10;j++)//钦定“四星确定时”
{
if(bit[j]!=2)
calc(i,j);
}
}
}
q0=u[0][0][0];
}
double dp[61][301][100][5];
//假设上限是 300 单抽 60 十连
int pre[61][301][100][5];
//正数表示选单抽 负数表示选十连
void init_dp()
{
for(int i=0;i<=60;i++)
for(int j=0;j<=300;j++)
for(int k=0;k<100;k++)
dp[i][j][k][0]=1;
for(int i=0;i<=60;i++)
{
for(int j=0;j<=300;j++)
{
if(!i&&!j) continue;
for(int k=0;k<100;k++)
{
for(int l=1;l<=4;l++)
{
double c1=0,c2=0;
if(j)
{
if(k!=99)
{
c1+=dp[i][j-1][k+1][l]*(1-qn);
c1+=dp[i][j-1][0][l]*qn*(1-qup);
c1+=dp[i][j-1][0][l-1]*qn*qup;
}
else
{
c1+=dp[i][j-1][0][l]*(1-qup);
c1+=dp[i][j-1][0][l-1]*qup;
}
}
if(i)
{
if(k<=89)
{
c2+=dp[i-1][j][k+10][l]*q0;
for(int n=0;n<l;n++)
for(int m=std::max(1,n);m<=10;m++)
c2+=dp[i-1][j][10-m][l-n]*u[0][n][m];
for(int n=l;n<=10;n++)
for(int m=n;m<=10;m++)
c2+=u[0][n][m];
}
else
{
for(int n=0;n<l;n++)
for(int m=std::max(1,n);m<=10;m++)
c2+=dp[i-1][j][10-m][l-n]*u[100-k][n][m];
for(int n=l;n<=10;n++)
for(int m=n;m<=10;m++)
c2+=u[100-k][n][m];
}
}
if(c1>c2) dp[i][j][k][l]=c1,pre[i][j][k][l]=1;
else dp[i][j][k][l]=c2,pre[i][j][k][l]=-1;
}
}
}
}
}
void init_path()
{
for(int i=0;i<=60;i++)
{
for(int j=0;j<=300;j++)
{
if(!i&&!j) continue;
for(int k=0;k<100;k++)
{
for(int l=1;l<=4;l++)
{
if(pre[i][j][k][l]>0)
{
if(k<99&&j&&pre[i][j-1][k+1][l]>0)
pre[i][j][k][l]=pre[i][j-1][k+1][l]+1;
}
else
{
if(k<90&&i&&pre[i-1][j][k+10][l]<0)
pre[i][j][k][l]=pre[i-1][j][k+10][l]-1;
}
}
}
}
}
}
bool ck(int n,int m,int k)
{
return !(n>=0&&n<=60&&m>=0&&m<=300&&k>=0&&k<100);
}
int main()
{
puts("使用说明:请输入目前所拥有的十连数量 n,单抽数量 m,保底页面状态k/100...");
puts("注意:每抽出一个四星,需要重新确定策略!");
puts("请保证 0<=n<=60,0<=m<=300,0<=k<100 且 n m k 均为整数...");
puts("请使用空格隔开 n m k");
puts("示例:“22 130 0” (不包含引号)");
puts("初始化中...");
init_fu();
init_dp();
init_path();
puts("初始化完成!");
int n,m,k,l;
scanf("%d%d%d",&n,&m,&k);
while(ck(n,m,k))
{
puts("数据非法,请重新输入!");
scanf("%d%d%d",&n,&m,&k);
}
puts("目前你的状态采用最佳策略后可以“至少获得一孔”、“至少获得两孔”、“至少获得三孔”、“至少获得四孔”的概率分别为:");
printf("%.8lf %.8lf %.8lf %.8lf\n",dp[n][m][k][1],dp[n][m][k][2],dp[n][m][k][3],dp[n][m][k][4]);
puts("请输入一个 1 到 4 之间到整数以选择你的期望孔数");
puts("(建议选择至少达到50%以上概率的孔数,不同期望孔数策略微不同)");
scanf("%d",&l);
while(l<=0||l>=5)
{
puts("数据非法,请重新输入!");
scanf("%d",&l);
}
while(n||m)
{
if(pre[n][m][k][l]>0)
{
printf("请连续使用%d张单抽券,直到抽出四星或者抽完为止\n",pre[n][m][k][l]);
}
else
{
printf("请连续使用%d张十连券,直到抽出四星或者抽完为止\n",-pre[n][m][k][l]);
}
puts("请重新输入你的状态 n m k");
puts("输入 -1 可结束");
scanf("%d",&n);
if(n==-1) exit(0);
scanf("%d",&m);
if(m==-1) exit(0);
scanf("%d",&k);
if(k==-1) exit(0);
while(ck(n,m,k))
{
puts("数据非法,请重新输入!");
scanf("%d%d%d",&n,&m,&k);
}
puts("目前你的状态采用最佳策略后可以“至少再获得一孔”、“至少再获得两孔”、“至少再获得三孔”、“至少再获得四孔”的概率分别为:");
printf("%.8lf %.8lf %.8lf %.8lf\n",dp[n][m][k][1],dp[n][m][k][2],dp[n][m][k][3],dp[n][m][k][4]);
puts("请输入一个 1 到 4 之间到整数以选择你的期望孔数");
puts("(建议选择至少达到50%以上概率的孔数,不同期望孔数策略微不同)");
scanf("%d",&l);
puts("输入 -1 可结束");
if(k==-1) exit(0);
while(l<=0||l>=5)
{
puts("数据非法,请重新输入!");
scanf("%d",&l);
}
}
return 0;
}
蒙特卡洛验证(采用简单策略1与暴力dp作对比)
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
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>
#include <ctime>
double qn=0.01,qup=0.6,q0;
double u[11][11][11];//u[0]即f
int bit[11];
void calc(int p3,int p4)
{
double mp=1;
int cntup=0,las=0;
for(int i=1;i<=10;i++)
{
cntup+=!bit[i];
if(i==p3) qn=0.02;
if(i==p4&&!las) qn=1;
if(bit[i]<2) las=i;
if(bit[i]==0) mp*=qn*qup;
else if(bit[i]==1) mp*=qn*(1-qup);
else mp*=1-qn;
if(i==p3||i==p4) qn=0.01;
}
u[p4][cntup][las]+=mp/10.0;
}
void init_fu()
{
for(int s=0;s<59049;s++)
{
int sta=s;
//0 up 1 四非up 2 非四
for(int i=1;i<=10;i++) bit[i]=sta%3,sta/=3;
for(int i=1;i<=10;i++)//钦定“三星确定时”
{
for(int j=0;j<=10;j++)//钦定“四星确定时”
{
if(bit[j]!=2)
calc(i,j);
}
}
}
q0=u[0][0][0];
}
double dp[61][301][100][5];
//假设上限是 300 单抽 60 十连
int pre[61][301][100][5];
//正数表示选单抽 负数表示选十连
void init_dp()
{
for(int i=0;i<=60;i++)
for(int j=0;j<=300;j++)
for(int k=0;k<100;k++)
dp[i][j][k][0]=1;
for(int i=0;i<=60;i++)
{
for(int j=0;j<=300;j++)
{
if(!i&&!j) continue;
for(int k=0;k<100;k++)
{
for(int l=1;l<=4;l++)
{
double c1=0,c2=0;
if(j)
{
if(k!=99)
{
c1+=dp[i][j-1][k+1][l]*(1-qn);
c1+=dp[i][j-1][0][l]*qn*(1-qup);
c1+=dp[i][j-1][0][l-1]*qn*qup;
}
else
{
c1+=dp[i][j-1][0][l]*(1-qup);
c1+=dp[i][j-1][0][l-1]*qup;
}
}
if(i)
{
if(k<=89)
{
c2+=dp[i-1][j][k+10][l]*q0;
for(int n=0;n<l;n++)
for(int m=std::max(1,n);m<=10;m++)
c2+=dp[i-1][j][10-m][l-n]*u[0][n][m];
for(int n=l;n<=10;n++)
for(int m=n;m<=10;m++)
c2+=u[0][n][m];
}
else
{
for(int n=0;n<l;n++)
for(int m=std::max(1,n);m<=10;m++)
c2+=dp[i-1][j][10-m][l-n]*u[100-k][n][m];
for(int n=l;n<=10;n++)
for(int m=n;m<=10;m++)
c2+=u[100-k][n][m];
}
}
if(c1>c2) dp[i][j][k][l]=c1,pre[i][j][k][l]=1;
else dp[i][j][k][l]=c2,pre[i][j][k][l]=-1;
}
}
}
}
}
void init_path()
{
for(int i=0;i<=60;i++)
{
for(int j=0;j<=300;j++)
{
if(!i&&!j) continue;
for(int k=0;k<100;k++)
{
for(int l=1;l<=4;l++)
{
if(pre[i][j][k][l]>0)
{
if(k<99&&j&&pre[i][j-1][k+1][l]>0)
pre[i][j][k][l]=pre[i][j-1][k+1][l]+1;
}
else
{
if(k<90&&i&&pre[i-1][j][k+10][l]<0)
pre[i][j][k][l]=pre[i-1][j][k+10][l]-1;
}
}
}
}
}
}
int cnt[1001],opt[1001];
int getc(int p0,int pup)
{
int res=rand()%1000+1;
if(res<=pup) return 0;
if(res<=p0) return 1;
return 2;
}
void work(int n,int m)
{
int bd=0,ct=0,res;
while(n||m)
{
if((bd+10<100&&n)||m==0)
{
int loc=rand()%10+1;
for(int j=1;j<=10;j++)
{
++bd;
if(bd==100)
res=getc(1000,600);
else if(j==loc)
res=getc(20,12);
else
res=getc(10,6);
if(res<2) bd=0;
if(res==0) ++ct;
}
--n;
}
else
{
++bd;
if(bd==100)
res=getc(1000,600);
else
res=getc(10,6);
if(res<2) bd=0;
if(res==0) ++ct;
--m;
}
}
++cnt[ct];
}
void work2(int n,int m)
{
int k=0,res,ct=0;
while(n||m)
{
for(int l=4;~l;l--)
{
if(dp[n][m][k][l]>0.5)
{
if((pre[n][m][k][l]>0&&m)||n==0)
{
++k;
if(k==100)
res=getc(1000,600);
else
res=getc(10,6);
if(res<2) k=0;
if(res==0) ++ct;
--m;
}
else
{
int loc=rand()%10+1;
for(int j=1;j<=10;j++)
{
++k;
if(k==100)
res=getc(1000,600);
else if(j==loc)
res=getc(20,12);
else
res=getc(10,6);
if(res<2) k=0;
if(res==0) ++ct;
}
--n;
}
break;
}

}
}
++cnt[ct];
}
int main()
{
puts("初始化中...");
init_fu();
init_dp();
init_path();
puts("初始化完成!");
srand(time(0));
int t=20;
puts("先输十连后输单抽,保证总抽数小于1000");
while(t--)
{
int n,m;
scanf("%d%d",&n,&m);

memset(cnt,0,sizeof cnt);
int T=1e6;
while(T--) work(n,m);
for(int i=n*10+m;i;i--) cnt[i]+=cnt[i+1];
//for(int i=n+m*10;i>4;i--) cnt[4]+=cnt[i];
for(int i=1;i<=4;i++)
printf("%lf\n",cnt[i]/1e6);

memset(cnt,0,sizeof cnt);
T=1e6;
//int l=4;
while(T--) work2(n,m);
for(int i=n*10+m;i;i--) cnt[i]+=cnt[i+1];
//for(int i=n+m*10;i>4;i--) cnt[4]+=cnt[i];
for(int i=1;i<=4;i++)
printf("%lf\n",cnt[i]/1e6);
}

return 0;
}
蒙特卡洛验证(仅仅是随机使用单抽与十连)
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
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <ctime>
#include <algorithm>
int cnt[1001],opt[1001];
int getc(int p0)
{
int res=rand()%1000+1;
if(res<=p0*6/10) return 0;
if(res<=p0) return 1;
return 2;
}
const int qn=100;
void work(int n,int m)
{
std::swap(n,m);
int bd=0,ct=0,q=0;
while(n||m)
{
opt[++q]=((rand()%(n+m)+1)<=n);
if(opt[q]==1) --n;
else --m;
}
for(int res,i=1;i<=q;i++)
{
if(opt[i]==1)
{
++bd;
if(bd==100)
res=getc(1000);
else
res=getc(qn);
if(res<2) bd=0;
if(res==0) ++ct;
}
else
{
int loc=rand()%10+1;
if(bd+10<100)
{
for(int j=1;j<=10;j++)
{
++bd;
if(j!=loc)
res=getc(qn);
else
res=getc(qn+10);
if(res<2) bd=0;
if(res==0) ++ct;
}
}
else
{
for(int j=1;j<=10;j++)
{
++bd;
if(bd==100)
res=getc(1000);
else if(j==loc)
res=getc(qn+10);
else
res=getc(qn);
if(res<2) bd=0;
if(res==0) ++ct;
}
}
}
}
++cnt[ct];
}
int main()
{
srand(time(0));
int t=20;
puts("先输十连后输单抽,保证总抽数小于1000");
while(t--)
{
int n,m;
scanf("%d%d",&n,&m);
memset(cnt,0,sizeof cnt);
int T=1e6;
while(T--) work(n,m);
for(int i=n*10+m;i;i--) cnt[i]+=cnt[i+1];
//for(int i=n+m*10;i>4;i--) cnt[4]+=cnt[i];
for(int i=1;i<=4;i++)
printf("%lf\n",cnt[i]/1e6);
}

return 0;
}
关于简单策略2的dp
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
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
#include <cstdio>
#include <algorithm>
double qn=0.01,qup=0.6,q0;
double u[11][11][11],g[10][100][5],F[11];//u[0]即f
bool isg[10][100][5];
int bit[11];
void calc(int p3,int p4)
{
double mp=1;
int cntup=0,las=0;
for(int i=1;i<=10;i++)
{
cntup+=!bit[i];
if(i==p3) qn=0.02;
if(i==p4&&!las) qn=1;
if(bit[i]<2) las=i;
if(bit[i]==0) mp*=qn*qup;
else if(bit[i]==1) mp*=qn*(1-qup);
else mp*=1-qn;
if(i==p3||i==p4) qn=0.01;
}
if(p4==0) F[cntup]+=mp/10.0;
u[p4][cntup][las]+=mp/10.0;
}
void init_fu()
{
for(int s=0;s<59049;s++)
{
int sta=s;
//0 up 1 四非up 2 非四
for(int i=1;i<=10;i++) bit[i]=sta%3,sta/=3;
for(int i=1;i<=10;i++)//钦定“三星确定时”
{
for(int j=0;j<=10;j++)//钦定“四星确定时”
{
if(bit[j]!=2)
calc(i,j);
}
}
}
q0=u[0][0][0];
}
double dfsg(int I,int J,int L)
{
if(L==0) return 1;
if(I==0&&J==0) return 0;
if(isg[I][J][L]) return g[I][J][L];
double ret=0;
if(I>0)
{
for(int i=0;i<=10;i++)
ret+=dfsg(I-1,J,std::max(0,L-i))*F[i];
}
else ret=dfsg(0,J-1,L-1)*qn*qup+dfsg(0,J-1,L)*(1-qn*qup);
g[I][J][L]=ret;
isg[I][J][L]=1;
return ret;
}
double dp[80][600][10][5];
bool is[80][600][10][5];
double dfs(int I,int J,int K,int L)
{
if(L==0) return 1;
if(I==0&&J==0) return 0;
if(is[I][J][K][L]) return dp[I][J][K][L];
int pos=10-K;
double ret=0;
if(I>=9)
{
double mp=1;
for(int i=1;i<=9;i++)//第几次出货
{
for(int j=1;j<=10;j++)//四星末尾位置
{
for(int n=0;n<=j;n++)//出几张
{
ret+=dfs(I-i,J,10-j,std::max(0,L-n))*u[0][n][j]*mp;
}
}
mp*=q0;
}
double s1=0,s2=0;
if(I>=10)
{
for(int j=1;j<=10;j++)
{
for(int n=pos;n<=j;n++)
s1+=dfs(I-10,J,10-j,std::max(0,L-n))*u[pos][n][j]*mp;
}
}
for(int j=1;j<=std::min(J,pos);j++)
{
if(j!=pos)
{
s2+=dfs(I-9,J-j,0,L-1)*mp*qn*qup+dfs(I-9,J-j,0,L)*mp*qn*(1-qup);
mp*=(1-qn);
}
else
s2+=dfs(I-9,J-j,0,L-1)*mp*qup+dfs(I-9,J-j,0,L)*mp*(1-qup);
}
ret+=std::max(s1,s2);
}
else if(I*10+J+K>=100)
{
double mp=1;
for(int i=1;i<=I;i++)
{
for(int j=1;j<=10;j++)
{
for(int n=0;n<=j;n++)
{
ret+=dfs(I-i,J,10-j,std::max(0,L-n))*u[0][n][j]*mp;
}
}
mp*=q0;
}
for(int j=1;j<=100-I*10-K;j++)
{
if(j!=100-I*10-K)
{
ret+=dfs(0,J-j,0,L-1)*mp*qn*qup+dfs(0,J-j,0,L)*mp*qn*(1-qup);
mp*=(1-qn);
}
else
ret+=dfs(0,J-j,0,L-1)*mp*qup+dfs(0,J-j,0,L)*mp*(1-qup);
}
}
else ret=dfsg(I,J,L);
dp[I][J][K][L]=ret;
is[I][J][K][L]=1;
return ret;
}
int main()
{
init_fu();
int t=20;
while(t--)
{
int a,b,c;
scanf("%d%d%d",&a,&b,&c);
for(int i=1;i<=4;i++)
printf("%lf\n",dfs(a,b,c,i));
}
return 0;
}
  • Copyright: Copyright is owned by the author. For commercial reprints, please contact the author for authorization. For non-commercial reprints, please indicate the source.
  • Copyrights © 2020-2022 Dew
  • Visitors: | Views:

请我喝杯咖啡吧~

支付宝
微信