i.Chiitoitsu

题目大意:

初始手牌有 13 张麻将牌,相同牌至多出现 2 张 每轮可以从牌堆摸牌,若达成七对子则自摸胡牌 若不然则选择手牌中某张牌并丢弃之 给定初始手牌,求最优策略下达成七对子的期望轮数 多组数据,数据组数不超过 105 组。

解决方法:

看了题解才学会的,只能说我真的太笨了,打了一年的acm还是一点长进都没有只能这么说。现在根据题解的意思给大伙分析一下。

这题采用的方法是概率dp,这个很好看出来。感觉概率+事件发生不确定性即可能被自我意识操控做出的决定与后面事件的发展概率相关联的题目就大概是概率dp了。。。。萌新胡说的,大佬别当真,如有更好的解释希望大佬赐教。那么对于dp来讲,我们首先是要先确认初始状态,初始状态肯定一张单牌都没有,这样的时候是回合0,不需要任何回合胡牌了,因为我们已经胡了。那接下来分析一下如何从这种裸状态推导到一开始的状态。

首先因为初始牌有13张,我们可以很轻易的得知不可能存在偶数张单牌,因此我们只要考虑单牌是奇数情况就可以了。

然后我们从前往后推的公式是    f[i,j]=\begin{cases} 1+f[i,j-1]*\frac{j-3}{j}+0*\frac{3}{j} & \text{ if } x=1 \\ 1+f[i-2,j-1]*\frac{3*i}{j}+f[i,j-1]*\frac{j-3*i}{j} & \text{ if } x>1 \end{cases}

为什么是这个公式呢,我们先来分析一下i==1的情况,i是单牌的数量,j是总共还剩下多少牌。我们的策略是,如果你此时已经有凑对的牌就不用管他们,就当已经扔掉了,如果摸上来一张不能凑对的就把它扔掉,否则就把一张单牌扔掉。那么摸上来一张不能凑对的概率是\frac{j-3*i}{j},摸上来一张能够凑对的概率是\frac{3*i}{j}.如果只剩一张单牌的话,那我如果摸到了就只需要一次就达成了,那么我在需要的回合数就是0。如果说没达成就需要新的牌减一的回合数。因此i==1的公式就其实和下面那个是一样的,只不过省略掉了前面的f[i-2,j-1],因为这个时候早就达成目标不需要i-2,因为不需要弃牌,所以真正当i==1的时候的0其实是f[0,j-1]。

由上同等可知,可知道当i>1时候的场景,所以不多加赘述。为什么一张不能凑对概率是这样,因为我们所有单张全都是初始给的,而一旦扔掉的单张我们是不可能重新捡回来了,因此单张的剩余数目只可能是3张。最终答案肯定是初始状态,证明完毕,下面是代码:

#include<bits/stdc++.h>
using namespace std;
#define int long long
char s[200005];
map<string,int> mp;
int p=1e9+7;
int km(int x,int n){
    int res=1;
    while(n){
        if(n%2) res=x*res%p;
        x=x*x%p;
        n>>=1;
    }
    return res;
}
int f[15][200];
void init(){
    for(int i=1;i<=13;i+=2)
        for(int j=1;j<=123;j++){
            if(i==1)
                f[i][j]=(1+((j-3)*km(j,p-2)%p)*f[i][j-1])%p;
            else
                f[i][j]=(1+(3ll*i*km(j,p-2)%p)*f[i-2][j-1]%p+((j-3*i)*km(j,p-2)%p)*f[i][j-1]%p)%p;
        }
}
signed main(){
    ios::sync_with_stdio(false);
    int t;
    cin>>t;
    init();
    int t1=0;
    while(t1<t){
        t1++;
        cin>>(s+1);
        mp.clear();
        for(int i=2;i<=26;i+=2){
            string s1="";
            s1+=s[i-1];
            s1+=s[i];
            mp[s1]++;
        }
        int cnt=0;
        for(auto u:mp){
            if(u.second==1ll)
                cnt++;
        }
        cout<<"Case #"<<t1<<":"<<" "<<f[cnt][123]<<"\n";
    }
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值