2022牛客暑期多校训练营1—I. Chiitoitsu(期望DP)

发布于:2023-01-05 ⋅ 阅读:(155) ⋅ 点赞:(0)

题目梗概

34种麻将牌,每种牌用一个数字和一个小写字母表示,每种4张,共计136张;

初始从中摸取13张牌;

每次操作从未被摸取过的牌堆中摸取一张;

若此时手中有7对牌,则胡牌结束游戏,否则丢出一张牌后继续重复操作,直到胡牌。

给出初始手上的牌,求至少还要多少次操作才能胡牌,结果对10^{9}+7取模。

输入形式

输入的第一行为一个正整数T \left ( 1\leq T\leq 10^{5} \right ),表示测试数据的组数;

接下来T行,每行为一个字符串,长度为26,表示初始的13张牌。

输出形式

对于第i组测试数据,输出一行,格式为“Case #i: x\n”,x为结果对10^{9}+7取余的数值。

样例

输入样例 输出样例
2
1m9m1p9p1s9s1z2z3z4z5z6z7z
1m1m4m5m1p4m2m2m2p2p2s2s2z
Case #1: 927105416
Case #2: 100000041

分析

该题为一个期望DP的问题,那么从以下角度分析

dp[i][j]表示牌堆中还有i张未被摸取的牌且手上还有j张单牌需要凑对时达到胡牌所需要的最小操作步数。

每次摸牌有两种情况

摸到1张新的单牌;

摸到的牌可以和手中的一张牌凑对。

则即可这样分类

当摸到一张新单牌时,则dp[i][j]=dp[i-1][j]+1

摸到的牌可以和手中已有的牌凑对时dp[i][j]=dp[i-1][j-2]+1

此时可知上述两种情况的概率分别为\frac{i-3j}{i}\frac{3j}{i},要注意的时j=1时再摸到一张可凑对的牌则直接结束游戏了,即可得到状态转移方程为

dp[i][j]=\left\{\begin{matrix} \frac{i-3j}{i}(dp[i-1][j]+1)+\frac{3j}{i}(dp[i-1][j-2]+1),j> 1\\ \frac{i-3j}{i}(dp[i-1][j]+1)+\frac{3j}{i},j=1 \end{matrix}\right.

还可以化简得到

dp[i][j]=\left\{\begin{matrix} \frac{i-3j}{i}dp[i-1][j]+\frac{3j}{i}dp[i-1][j-2]+1,j> 1\\ \frac{i-3j}{i}dp[i-1][j]+1,j=1 \end{matrix}\right.

由题意,初始摸取13张牌后牌堆中还剩123张牌,设初始手上有n张单牌,则答案即为dp[123][n]

C++代码

#include<bits/stdc++.h>
#define endl '\n'
#define int long long
using namespace std;

map<string, int> cnt;
int t, dp[150][15];
const int mod = 1e9 + 7;

int ksm(int x, int y, int mod)
{
	int ans = 1, tmp = x;;
	while (y)
	{
		if (y & 1) ans = ans * tmp % mod;
		y >>= 1;
		tmp = tmp * tmp % mod;
	}
	return ans;
}

int inv(int x)
{
	return ksm(x, mod - 2, mod);
}

int dfs(int i, int j)
{ 
	if (dp[i][j] != -1) return dp[i][j];
    if (j == 0) return 0;
    int p1 = (i- 3 * j) * inv(i) % mod;
    int p2 = (3 * j) * inv(i) % mod;
    if(j == 1) dp[i][j] = (p1 * dfs(i - 1, j) + 1) % mod;
    else dp[i][j] = (p1 * dfs(i - 1, j) % mod + p2 * dfs(i - 1, j - 2) % mod + 1) % mod;
    return dp[i][j];
}

signed main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
	cin >> t;
	memset(dp, -1, sizeof(dp));
	int cas = 0;
	while (t--)
	{
		string s;
		cin >> s;
		for (int i = 0; i < 13; i++)
		{
			string c = "";
			c += s[2 * i];
			c += s[2 * i + 1];
			cnt[c]++;
		}
		int n = 0;
		for (auto it : cnt)
		{
			if (it.second == 1) n++;
		}
		cout << "Case #" << ++cas << ": " << dfs(123, n) << endl;
		cnt.clear();
	}
}

网站公告

今日签到

点亮在社区的每一天
去签到