2022牛客暑期多校第一场 I. Chiitoitsu

2022牛客暑期多校第一场 I. Chiitoitsu

题意

日本麻将共有 34 种牌,每种牌各 4 张,共 136 张牌,给定一开始手上的 13 张牌,剩下的 123 张牌在牌堆中,每次随机从牌堆摸 1 张,摸完后若手上的 14 张牌是 7 对互不相同的对子,则游戏结束,否则需要在 14 张牌中选择 1 张丢弃(不放回牌堆,意味着牌堆中牌的数量会随着摸牌次数逐渐减少),继续上述过程,直到凑满 7 对互不相同的对子。

保证给定的一开始手上的 13 张牌,不会存在 1 种牌超过 3 张。

问,最优策略下的期望摸牌次数。

分析

容易有一个直观的想法:

已有的对子不丢弃。

若对于抽到的牌的种类已经在手里的牌中形成对子,则丢弃抽到的牌。

若抽到的牌在手上无法和已有的牌形成对子,丢弃最不可能在牌堆中再次抽到的那种牌。

若抽到的牌能和手上的某张牌形成对子,则配对,丢弃最不可能在牌堆中抽到的那种牌。

根据这个想法,对于一开始手上的每种牌不是 2 张,就是 1 张,牌堆里的每种牌只可能是 2 张、3 张或 4 张。

一开始,抽到剩余 3 张的必成对,抽到剩余 4 张的必不成对,剩下 3 张牌,此时丢掉其他单牌和丢掉抽到的这张单牌没有本质区别,都会多一种牌有 1 张是浪费掉的,且剩余 3 张都在牌堆里,其余都一样。之后,根据上面直观的想法,曾经被弃掉那种牌不可能再用它来形成对子。

所以最优策略可以贪心的认为,除去手上的对子以外,根据手上的单牌去凑对子。

这个时候,对于要凑成对子但还没凑成对子的牌,在牌堆里只可能剩下 3 张。这样,期望步数就和牌的种类无关,只和手上已经凑了几对牌,以及牌堆里还剩下几张牌有关。

定义状态 f(i,j) 为抽牌之前(手上有 13 张牌),牌堆里还剩 i 张牌,手上已经凑成 j 对对子的期望步数。

显然 3i123,0j6

由于还需要凑 7j 对牌,这种状态之下,只有 3(7j)i 是合法状态。

由于手上有 132j 张单牌,这种状态之下,

3(132j)i 时,接下来抽到的牌一定会和手上的牌成对。

3(132j)i 时,接下来抽到的牌有 3(132j)i 的概率和手上的牌成对,有 13(132j)i 的概率不和手上的牌成对。

根据上述分析,写出状态转移及初始条件:

i>30j5

f(i,j)={3(132j)if(i1,j+1)+(13(132j)i)f(i1,j)+1,3(132j)if(i1,j+1)+1,3(132j)>i

i>3j=6

f(i,j)=(13(132j)i)f(i1,j)+1

特别地,

f(3,6)=1

于是,最后只需要统计一开始手上的对子数 c,答案就是 f(123,c)

代码

#include <iostream>
#include <map>
#include <string>
using namespace std;
typedef long long Lint;
const int mod = 1e9 + 7;
inline int add(int a, int b) {
return (a += b) < mod ? a : a - mod;
}
inline int mul(int a, int b) {
return (Lint)a * b % mod;
}
int dp[7], inv[124];
void solve(int kase) {
string s;
map<string, int> num;
cin >> s;
for (int i = 0; i < s.size(); i += 2) {
num[s.substr(i, 2)]++;
}
int c = 0;
for (auto it : num)
c += (it.second == 2);
cout << "Case #" << kase << ": " << dp[c] << '\n';
}
int main() {
inv[1] = 1;
for (int i = 2; i <= 123; i++) {
inv[i] = mul(mod - mod / i, inv[mod % i]);
}
dp[6] = 1;
for (int i = 4; i <= 123; i++) {
for (int j = 0; j <= 5; j++) {
if (3 * (7 - j) > i)
continue;
if (3 * (13 - 2 * j) >= i) {
dp[j] = add(dp[j + 1], 1);
} else {
int p = mul(3 * (13 - 2 * j), inv[i]);
dp[j] = add(add(mul(p, dp[j + 1]), mul(add(1, mod - p), dp[j])), 1);
}
}
int p = mul(i - 3, inv[i]);
dp[6] = add(mul(p, dp[6]), 1);
}
int T;
ios::sync_with_stdio(0);
cin.tie(0);
cin >> T;
for (int i = 1; i <= T; i++)
solve(i);
return 0;
}

本文作者:聆竹听风

本文链接:https://www.cnblogs.com/Bamboo-Wind/p/16499831.html

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   聆竹听风  阅读(33)  评论(0编辑  收藏  举报
历史上的今天:
2021-07-20 HDU 6955. Xor Sum题解
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起