• Chiitoitsu(期望DP)


    题意

    总共有\(34\)种麻将牌,每种牌有\(4\)张。初始手牌有\(13\)张牌,相同牌至多出现\(2\)张。

    每轮可以从牌堆摸牌,若达成七对子则自摸胡牌,若不然则选择手牌中某张牌并丢弃之。

    给定初始手牌,求最优策略下达成七对子的期望轮数。

    (题目翻译来源于雨巨的讲解PPT)

    题目链接:https://ac.nowcoder.com/acm/contest/33186/I

    数据范围

    多组数据\(1 \leq T \leq 10^5\)

    思路

    期望DP,典型套路是定义一个转移数组\(f\),表示从该状态到最终状态的期望轮数。

    在这里,必须要有的一个状态是当前手里的对子数(或者是单牌数),为了能够计算概率,因此还需要知道牌堆里面还有多少牌,因此另外一个状态表示牌堆中还剩多少牌。

    因此,我们用\(f(i, j)\)表示牌堆中还有\(i\)张牌,并且手里有\(j\)个对子时,还需要多少次摸牌才能够胡牌的期望。

    在考虑递推式之前,先分析一下最优策略,即我们应该丢弃什么牌。显然,我们应该丢弃单牌,一旦成对了就不再丢弃。因此,我们可以推断,自己手牌中的单牌,在牌堆中一定有\(3\)张,因为只要从牌堆中再拿一张,那么就可以凑成对子了。

    现在考虑递推式。\(f(i, j) = p_1 f(i - 1, j) + p_2 f(i - 1, j + 1) + 1\),第一项表示摸到一张新的单牌,第二项表示可以和已有的单牌凑对。

    由于\(p_2\)容易计算,先算\(p_2\)\(p_2 = \frac{3(13 - 2j)}{i}\),其中\(13 - 2*j\)表示手上单牌种类。\(p_1 = \frac{i - 3(13-2j)}{i}\)

    记忆化搜索即可。

    代码

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #include <map>
    
    using namespace std;
    
    typedef long long ll;
    
    const int N = 150, M = 10, mod = 1e9 + 7;
    
    char s[N];
    map<string, int> mp;
    ll f[N][M];
    
    ll qmi(ll a, ll b)
    {
        ll res = 1;
        while(b) {
            if(b & 1) res = res * a % mod;
            a = a * a % mod;
            b >>= 1;
        }
        return res;
    }
    
    ll inv(ll x)
    {
        return qmi(x, mod - 2);
    }
    
    ll dfs(int a, int b)
    {
        ll &v = f[a][b];
        if(v >= 0) return v;
        ll t = a - 3 * (13 - 2 * b);
        if(t > 0) {
            ll tmp = inv(a);
            ll p1 = t * tmp % mod, p2 = 3 * (13 - 2 * b) * tmp % mod;
            v = (1 + p1 * dfs(a - 1, b) + p2 * dfs(a - 1, b + 1)) % mod;
        }
        else {
            v = (1 + dfs(a - 1, b + 1)) % mod;
        }
        return v;
    }
    
    int main()
    {
        int T;
        scanf("%d", &T);
        memset(f, -1, sizeof f);
        for(int i = 0; i < N; i ++) f[i][7] = 0;
        for(int cas = 1; cas <= T; cas ++) {
            scanf("%s", s + 1);
            mp.clear();
            for(int i = 1; i <= 13; i ++) {
                string t = "";
                t += s[2 * i - 1], t += s[2 * i];
                mp[t] ++;
            }
            int cnt = 0;
            for(auto p : mp) {
                if(p.second >= 2) cnt ++;
            }
            printf("Case #%d: %lld\n", cas, dfs(123, cnt));
        }
        return 0;
    }
    
  • 相关阅读:
    学习Hadoop不错的系列文章(转)
    浏览器的渲染原理简介
    大数据人才缺乏,你准备好了吗?
    SVN分支与合并透析
    Windows下SVN服务端(Subversion)及客户端(TortoiseSVN)详细安装教程
    maven2介绍(转)
    eclipse安装velocity插件(转)
    为大数据时代做好准备——来自《大数据的冲击》一书精彩片段(转)
    【VB】Format 格式化日期时间数字函数详解
    获取本机ID和电脑名称
  • 原文地址:https://www.cnblogs.com/miraclepbc/p/16596070.html
Copyright © 2020-2023  润新知