其他分享
首页 > 其他分享> > 2022牛客多校第一场 I.dp

2022牛客多校第一场 I.dp

作者:互联网

https://ac.nowcoder.com/acm/contest/33186/I

题意

日本麻将有34种牌,每种4张。给你13张牌,其中每种牌最多两张,最少0张。
一轮操作定义如下:
1.从剩余牌堆中任意抽取一张,现在共14张
2.如果刚好凑成7对牌,则结束游戏
3.从手牌中丢弃一张牌,继续下一轮。
一名玩家进行游戏,问:游戏的期望轮数是多少?

分析

容易发现,因为手牌中每种牌最多2张,因此最优策略是如果抽到的牌可以凑对,那么丢掉一张单牌;否则丢弃抽到的牌。
由于需要算概率,需要知道每种牌的剩余张数。
(⭐一个值得注意的点是,如果某个问题涉及到所有状态,比如每个决策都会对之后的决策产生影响时,不应该记录非常庞大数量的状态,而要从dp的角度考虑最优解)
此处就不应该记录每种牌的剩余张数
如果牌i有1张,说明剩余牌堆中肯定还有3张,可算概率;如果有0张或2张,那么概率为1-之前的,牌直接丢弃,所以不需要记录牌堆中还剩余几张

code

思路:\(dp[i][j]\)表示手牌中有i张单牌,剩余j张牌的抽取期望次数

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int mod = 1e9+7;
int t,n;
char s[55];
unordered_map<string,int>mp;
ll dp[22][205];
ll fac[205],invv[205];

ll qpow(ll a,ll b){
	ll ans=1;
	while(b){
		if(b%2){
			ans*=a;
			ans%=mod; 
		}
		a*=a;
		a%=mod;  //ע������Ҳ��Ҫȡģ 
		b/=2;
	}
	return ans;
}

inline ll inv(int x){
    return invv[x];
}

void init(int N){
    for(int i=1;i<=N;i++) invv[i]=qpow(i,mod-2);
}

int main(){
    init(130);
    cin>>t;
    for(int tt=1;tt<=t;tt++){
        scanf("%s",s);
        mp.clear();
        int cnt=13;
        for(int i=0;i<strlen(s);i+=2){
            string tem="";
            tem+=s[i];tem+=s[i+1];
            mp[tem]++;
            if(mp[tem]==2) cnt-=2;
        }

        dp[1][3]=1;
        for(int i=1;i<=cnt;i+=2){
            for(int j=4;j<=123;j++){
                if(i==1){
                    dp[i][j]=1+(j-3)*inv(j)%mod*dp[i][j-1]%mod;
                    dp[i][j]%=mod;
                }
                else{
                    dp[i][j]=1+3*i*inv(j)%mod*dp[i-2][j-1]%mod+(j-3*i)*inv(j)%mod*dp[i][j-1]%mod;
                    dp[i][j]%=mod;
                }
            }
        }

        printf("Case #%d: %lld\n",tt,dp[cnt][123]);
    }

    system("pause");
    return 0;

}

标签:剩余,每种,int,ll,多校,牛客,dp,mod
来源: https://www.cnblogs.com/re0acm/p/16501157.html