题意:
1.初始手牌有 13 张麻将牌,相同牌至多出现 2 张
2.所有的牌有34种,每种牌都有4张
3.每轮可以从牌堆摸牌,若达成七对子则自摸胡牌
4.如果没有达成七对子则选择手牌中某张牌并丢弃(包含本轮摸到的牌)
5.给定初始手牌,求最优策略下达成七对子的期望轮数(输出mod 1e9 + 7)
6.多组数据,数据组数不超过 1e5 组
题目分析:
本题求数学期望,考虑使用 d p dp dp思想解决:
1.我们设 d p [ i ] [ j ] dp[i][j] dp[i][j]表示摸了 i i i张牌后还剩下j张没凑成对子的牌;
2.本题要求最优策略,又由于初始手牌中相同的牌不会超过两张,所以我们进行贪心及:如果摸到的牌不能和手牌中的单牌组成对子,则会丢掉新进的牌,否则随机丢一张手中的单牌
所以我们每次摸牌时会出现两种情况:
1.摸到的牌可以和手牌中的单牌组成对子,此事件发生的概率就是 3 j 124 − i \frac{3j}{124 - i} 124−i3j(第 i i i次摸牌时手上有 j j j张单牌)
2.摸到的牌不可以和手牌中的单牌组成对子,此事件发生的概率就是 124 − i − 3 j 124 − i \frac{124 - i - 3j}{124 - i} 124−i124−i−3j
(注:由于初始时有13张牌,所以剩下123张牌,所以第 i i i次摸牌时还有 124 − i 124 - i 124−i 张牌,我们一直进行最优操作所以手中单牌在剩下的牌中还有 $3j $ 张,不是手中的单牌的数量 124 − i − 3 j 124 - i - 3j 124−i−3j)
根据以上分析我们的得到状态转移方程:
(注:加号前表示第
i
i
i次摸的牌不能构成对子的所有概率,及摸
i
−
1
i - 1
i−1次牌后还剩
j
j
j张单牌的概率乘第
i
i
i次摸牌不能构成对子的概率;加号后边表示第
i
i
i次摸的牌能构成对子的所有概率,及摸
i
−
1
i - 1
i−1次牌后还剩
j
+
2
j + 2
j+2(因为匹配成功后会少两张单牌)张单牌的概率乘第
i
i
i次摸牌能构成对子的概率)
由于我们方程求得时概率我们最终要知道期望:
根据数学期望的公式最终答案就是 :
(注: i i i次结束的概率就是 i − 1 i - 1 i−1次摸牌后剩下1张单牌的概率乘上第 i i i次摸牌可以组成对子的概率)
最后预处理求出初始手牌有(1~13)张单牌的所有情况(手上只会有奇数张单牌)
AC代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int, int> PII;
const int N = 200 + 10, mod = 1e9 + 7;
ll dp[N][N], ans[N], op[N];
ll qmi(ll x, ll k)
{
ll res = 1;
while (k)
{
if(k & 1) res = res * x % mod;
x = x * x % mod;
k >>= 1;
}
return res % mod;
}
map<string, int> mp;
int main(void)
{
for (int k = 1; k <= 13; k += 2)//初始手牌中的单牌数量
{
memset(dp, 0, sizeof(dp));
dp[0][k] = 1;//初始化第0次摸牌手牌中单牌数量为k的概率为1
for (int i = 1; i <= 121; ++i)//最多摸121次一定会胡牌
for (int j = 1; j <= 13; j += 2)
{
dp[i][j] = (((dp[i - 1][j + 2] * (j + 2) * 3) % mod + (dp[i - 1][j] * (124 - i - j * 3)) % mod) * qmi(124 - i, mod - 2)) % mod;//转移方程记得求逆元
}
ll res = 0;
for (int i = 1; i <= 121; ++i)
res = (res + (i * dp[i - 1][1] * 3) % mod * qmi(124 - i, mod - 2) % mod) % mod;//求期望
ans[k] = res;//ans[k]表示初始手上有k张单牌最终获胜的期望轮数
}
int t; cin >> t;
int res = 0;
while (t--)
{
string s;
cin >> s;
mp.clear();
int cnt = 13;
for (int i = 0; i < s.size(); i += 2)
{
string c = s.substr(i, 2);
mp[c]++;
if (mp[c] == 2) cnt -= 2;
}
cout << "Case #" << ++res << ": " << ans[cnt] << endl;
}
return 0;
}