简化题意
一副除去大小王的扑克牌,初始两人各拿五张,你知道自己的牌,可以换 掉其中的若干张牌并加注同等数量的筹码,最终总点数大的一方胜并赢得 全部筹码,问最大收益的期望。
问题转化为
47
47
47 张牌 ,抽
k
k
k 张换掉最小的
k
k
k 张,对方抽
5
5
5 张,获胜的期望,终止状态肯定是我抽
k
k
k 张牌,对方抽
5
5
5 张牌,定义
f
[
i
]
[
x
]
[
j
]
[
y
]
f[i][x][j][y]
f[i][x][j][y] 表示我抽了
i
i
i 张牌,得分是
x
x
x,对方抽了
j
j
j 张牌,得分为
y
y
y 的方案数,那么总方案数就是
T
k
=
∑
x
∈
[
0
,
65
]
∑
y
∈
[
0
,
65
]
f
[
k
]
[
x
]
[
5
]
[
y
]
=
∑
x
∈
[
0
,
65
]
,
y
∈
[
0
,
65
]
f
[
k
]
[
x
]
[
5
]
[
y
]
T_k=\sum_{x\in[0,65]}\sum_{y\in[0,65]} f[k][x][5][y]\\=\sum_{x\in[0,65],y\in[0,65]} f[k][x][5][y]
Tk=x∈[0,65]∑y∈[0,65]∑f[k][x][5][y]=x∈[0,65],y∈[0,65]∑f[k][x][5][y]
下面省略
x
x
x ,
y
y
y 的范围
那么我恰好得分为
x
x
x ,对手恰好得分为
y
y
y 的概率是
P
=
f
k
,
x
,
5
,
y
T
k
.
P \;=\; \frac{f_{k,x,5,y}}{T_k}.
P=Tkfk,x,5,y.
令
S
k
:
=
S_k:=
Sk:= 剩下的
5
−
k
5-k
5−k 张最大牌得分之和
如果 S k + x > y S_k+x>y Sk+x>y ,我赢,收益为 k k k
如果 S k + x < y S_k+x<y Sk+x<y ,我输,收益为 − k -k −k
针对所有
(
x
,
y
)
(x,y)
(x,y) 的期望,我们先把收益与概率相乘,再求和相加,故期望为
E
k
=
∑
S
k
+
x
>
y
(
+
k
)
f
k
,
x
,
5
,
y
T
k
⏟
赢的期望贡献
+
∑
S
k
+
x
<
y
(
−
k
)
f
k
,
x
,
5
,
y
T
k
⏟
输的期望贡献
.
E_k = \underbrace{ \sum_{\,S_k+x>y\,} (+k)\,\frac{f_{k,x,5,y}}{T_k} }_{\text{赢的期望贡献}} \;+\; \underbrace{ \sum_{\,S_k+x<y\,} (-k)\,\frac{f_{k,x,5,y}}{T_k} }_{\text{输的期望贡献}}.
Ek=赢的期望贡献
Sk+x>y∑(+k)Tkfk,x,5,y+输的期望贡献
Sk+x<y∑(−k)Tkfk,x,5,y.
由于
k
k
k 和
T
k
T_k
Tk 都和
(
x
,
y
)
(x,y)
(x,y) 无关,可提出到求和符号外,故
E
k
=
∑
s
k
+
x
>
y
f
k
,
x
,
5
,
y
∑
x
,
y
f
k
,
x
,
5
,
y
k
+
∑
s
k
+
x
<
y
f
k
,
x
,
5
,
y
∑
x
,
y
f
k
,
x
,
5
,
y
(
−
k
)
.
E_k= \frac{ \sum_{s_k+x>y} f_{k,x,5,y} }{ \sum_{x,y}f_{k,x,5,y} }\;k \;+\; \frac{ \sum_{s_k+x<y} f_{k,x,5,y} }{ \sum_{x,y}f_{k,x,5,y} }\;(-k).
Ek=∑x,yfk,x,5,y∑sk+x>yfk,x,5,yk+∑x,yfk,x,5,y∑sk+x<yfk,x,5,y(−k).
现在考虑如何求DP数组,开成这样的DP数组,需要积累的是一般都需要原地更新,意味着正向递推会重复计算,所以每一维都需要倒序计算,或者写一个副本,每次DP完swap
#pragma GCC optimize(3,"Ofast","inline")
#include<bits/stdc++.h>
using namespace std;
using i64 = long long;
using i128 = __int128;
constexpr int mod = 998244353;
i64 qp(i64 a,i64 b){
i64 ans = 1;
while(b){
if(b&1) ans = ans*a%mod;
a = a*a%mod;
b>>=1;
}
return ans;
}
i64 f[6][66][6][66];//J抽了i张点数是x的方案D抽了j张点数是y的方案数
int norm(string &s){
if(s=="A") return 1;
if(s=="10") return 10;
if(s=="J") return 11;
if(s=="Q") return 12;
if(s=="K") return 13;
return s[0]-'0';
}
int cnt[15];
signed main(){
ios::sync_with_stdio(0);cin.tie(0);
f[0][0][0][0]=1;
vector<int> my(5);
for(int i = 0;i<5;++i){
string c;
cin>>c;
cnt[norm(c)]++;
my[i] = norm(c);
}
vector<int> a;
for(int i = 1;i<=13;++i){
for(int j = 1;j<=4-cnt[i];++j){
a.emplace_back(i);
}
}
//所有都需要倒序枚举
for(auto &z:a){
for(int i = 5; i >= 0; --i){
for(int x = 65; x >= 0; --x){
for(int j = 5; j >= 0; --j){
for(int y = 65; y >= 0; --y){
// 1) 不要这张牌:什么都不做,f[i][x][j][y] 保持
// 2) 给我补牌
if(i+1 <= 5 && x+z <= 65){
f[i+1][x+z][j][y] = (f[i+1][x+z][j][y] + f[i][x][j][y]) % mod;
}
// 3) 给对手补牌
if(j+1 <= 5 && y+z <= 65){
f[i][x][j+1][y+z] = (f[i][x][j+1][y+z] + f[i][x][j][y]) % mod;
}
}
}
}
}
}
sort(my.begin(),my.end(),greater());
for(int k = 1;k<=5;++k){//换掉k张
i64 ans = 0;
i64 sk =0;
for(int i = 0;i<5-k;++i) sk+=my[i];
i64 frac =0,f1=0,f2=0;
for(int x = 0;x<=65;++x){
for(int y = 0;y<=65;++y){
(frac+=f[k][x][5][y])%=mod;
if(sk+x>y) (f1+=f[k][x][5][y])%=mod;
if(sk+x<y) (f2+=f[k][x][5][y])%=mod;
}
}
frac = qp(frac,mod-2);
ans = (k*f1%mod*frac%mod-k*f2%mod*frac%mod+mod)%mod;
cout<<ans<<"\n";
}
return 0;
}