牛客多校第一场i题 Chiitoitsu 期望+概率DP

tag:数学期望,DFS,STL,费马最小定理,分数取模,概率DP。
Chiitoitsu
题面过于巨大,我说一下题意。

34种手牌每种4张共136张手牌,初始情况下发给你13张手牌,每回合你都做如下操作
1.摸牌
2.看看是不是手里现在14张牌凑成7个对子,是的结束游戏
3.扔牌

题目保证给定的初始手牌没有同一种手牌会超过两张的情况。

问:用x轮结束游戏,x的期望是多少,x取模于1e9+7

首先确定积极的游戏策略是:
摸牌后,如果摸到了可以和目前手牌组成对子的牌,留着,并且丢掉一张落单的手牌。
否则把摸到的这张牌再丢了。

然后我们来推算怎么去推这个期望。
就按照样例来分析。

1m1m4m5m1p4m2m2m2p2p2s2s2z

现在落单的手牌只有三个,目标是配出三对。
第一把摸牌,成功配对概率3*(13-5*2)/123 也就是:目标手牌/牌堆里总牌数。成功配对的概率就是反着来的。

第二把摸牌的概率又要与第一把配对的情况来决定,这就不免让我们联想起这是一个递推的过程。

从第一把推到第n把,然后把算出的根据每一层的概率算出期望。

算出所有的概率,再去层层算出期望即可。

详情见代码,建议先用脑子想想初始手牌就有6个对子的时候期望是怎么算出来的

#include<bits/stdc++.h>
#define enld '\n'
#define int long long
using namespace std;
const int N = 123;
const int mod = 1e9+7;
int t,con;
string str;
map<string,int> mp;
int dp[N][7];//a total of 34*4=136 tiles 初始手牌有j对牌,现在已经摸了i张牌,配成7对还需要的回合数的期望
int inv[140];
int DFS(int round,int pair)
{
    if(dp[round][pair]!=-1)//remember
        return dp[round][pair];
    int rem=136-13-round;//牌堆中有多少手牌
    int valid=3*(13-pair*2);//手里面落单的手牌还有(13-pair*2)张,牌堆里还有三倍这么多,这些是我们想摸的牌,也就是成功配对概率里的分子
    int ans=1,p=1LL*valid*inv[rem]%mod;
    if(pair!=6)
        ans=(ans%mod+p*DFS(round+1,pair+1)%mod)%mod;//成功配对的情况
    if(valid<rem)
        ans=(ans%mod+(mod+1-p)*DFS(round+1,pair)%mod)%mod;//没成功配对的情况,同时最后一对,第七对配对成功就是越过这个if判断的情况,而为什么不加上这最后一个对子的贡献呢?可以自己按照初始手牌是6个对子的情况模拟一下就知道了。
    //ans是什么,是我从pair对配到7对总共的期望值。
    return dp[round][pair]=ans;//返回现在是什么情况
}
int fastpow(int n,int a,int ans=1)
{
    while(n){
        if(n&1)
            ans=ans*a%mod;
        a=a*a%mod;
        n>>=1;
    }
    return ans%mod;
}
signed main()
{
    memset(dp,-1,sizeof(dp));
    inv[1]=1;
    for(int i=2;i<=136;i++)
        inv[i]=fastpow(mod-2,i);//预先处理出
    for(int i=0;i<=6;i++)
        DFS(0,i);
    for(cin>>t;t;t--)
    {
        con++;
        cin>>str;
        mp.clear();
        for(int i=0;i<13;i++)
            mp[str.substr(2*i,2)]++;
        int cnt=0;
        for(auto p:mp)
            if(p.second==2)
                cnt++;
        printf("Case #%lld: %lld\n",con,dp[0][cnt]);
    }
    return 0;
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值