ALGORITHM NOTE / ARCHIVE
2025 ICPC Nanchang Invitational and Jiangxi Provincial Collegiate Programming Contest——C
一副除去大小王的扑克牌,初始两人各拿五张,你知道自己的牌,可以换 掉其中的若干张牌并加注同等数量的筹码,最终总点数大的一方胜并赢得 全部筹码,问最大收益的期望。
简化题意
一副除去大小王的扑克牌,初始两人各拿五张,你知道自己的牌,可以换 掉其中的若干张牌并加注同等数量的筹码,最终总点数大的一方胜并赢得 全部筹码,问最大收益的期望。
问题转化为 张牌 ,抽 张换掉最小的 张,对方抽 张,获胜的期望,终止状态肯定是我抽 张牌,对方抽 张牌,定义 表示我抽了 张牌,得分是 ,对方抽了 张牌,得分为 的方案数,那么总方案数就是
下面省略 , 的范围
那么我恰好得分为 ,对手恰好得分为 的概率是
令 剩下的 张最大牌得分之和
如果 ,我赢,收益为
如果 ,我输,收益为
针对所有 的期望,我们先把收益与概率相乘,再求和相加,故期望为
由于 和 都和 无关,可提出到求和符号外,故
现在考虑如何求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;
}