CCSP2019T2_紙牌計數 | 2019蘇州CCSP大學生計算機系統與程序設計競賽

題目描述

偶然在CSDN看到有人寫了CCSP2019T2_紙牌計數的題解,突然想起來是一個不錯的計數、dp題。
以前的U盤找不到了,記得當時存了一步步偏分到AC代碼,可惜。又想起來18年打鐵了。。。
此人的題解的鏈接 CCSP201902紙牌計數——解題報告
當年一共有5題,取自:https://www.sohu.com/a/347851686_610300
T1: 摘水果 fruit
T2:紙牌計數 card
T3:系統實現題 SQL 查詢
T4:系統策略題 調度器 scheduler
T5:系統結構體 評測魚 risc-v
T2的題面:

Description
我們有一副紙牌,它由 n 張牌組成,其中每張牌上標有一個數字(0 到 9)和一個大寫字母(A 到 Z),例如 2C、1F。

我們如下定義一個字符串與一張牌之間的匹配關係:
字符串 ?? 與任何一張牌都匹配。
第一位爲 ? 而第二位爲字母的字符串,與任何一張標有該字母的牌匹配。例如,字符串 ?C 與任何標有 C 的牌匹配。
第一位爲數字而第二位爲字母的字符串,僅與內容完全一致的牌匹配。例如,字符串 0C 與內容爲 0C 的牌匹配。
不會出現第一位爲數字而第二位爲 ? 的字符串。
我們稱 m 個字符串 S1 ... Sm 與 m 張牌 C1 ... Cm 是匹配的,當且僅當:存在集合 { 1 , … , m } 上的一一映射 σ,使得對於任意 1 ≤ i ≤ m ,Si 與 C_σ(i) 匹配。

例如,對於 ?? 和 ?C 這兩個字符串,可以匹配內容爲 1C 和 2C 的牌,因爲字符串 ?? 與內容爲 2C 的牌匹配且字符串 ?C 與內容爲 1C 的牌匹配,而 ?? 和 ?C 這兩個字符串不能匹配內容爲 1S 和 1P 的牌。

現在,給定 m 個字符串,你需要求解:如果在 n 張牌中(無放回地)隨機選取 m 張,有多大概率與這些字符串匹配?

Input
每個輸入文件包含多個輸入數據。
第一行輸入該文件的數據個數 T。
接下來依次輸入每個數據。每個數據包含三行,其中:
第一行輸入兩個正整數 n 和 m;
第二行輸入以空格分隔的 n 個字符串,表示每張紙牌的內容(每個字符串第一位爲數字,第二位爲大寫字母);
第三行輸入以空格分隔的 m 個字符串,表示每個需要匹配的字符串(每個字符串第一位爲數字,第二位爲大寫字母;或第一位爲 ?,第二位爲大寫字母;或爲 ??)。

Output
對於每個輸入數據,輸出一行,表示所求的概率。概率需要以最簡分數 u/v 的形式輸出,其中 0 ≤ u ≤ v 且 u, v 爲互質的整數。
特殊地,對於 0 請輸出 0/1,對於 1 請輸出 1/1。

Subtasks
對於所有的數據,1 <= m <= n <= 60, T <= 1000, $1 \leq m \leq n \leq 60; T \leq 1000$。
(11分)m=1;
(23分)m=n;
(27分)n=30 且所有的牌爲 0C 1C 2C 3C 4C 5C 6C 7C 8C 9C 0S 1S 2S 3S 4S 5S 6S 7S 8S 9S 0P 1P 2P 3P 4P 5P 6P 7P 8P 9P;
(22分)n=40 且所有的牌爲 0C 1C 2C 3C 4C 5C 6C 7C 8C 9C 0C 1C 2C 3C 4C 5C 6C 7C 8C 9C 0S 1S 2S 3S 4S 5S 6S 7S 8S 9S 0P 1P 2P 3P 4P 5P 6P 7P 8P 9P;
(17分)沒有特殊的限制。

題解

一時間不記得咋寫的了,按記憶口胡了一個做法,感覺複雜度有點假,但好像跑不滿,以後再看看吧:

  • 求概率/期望的時候,兩個獨立事件(即正交的情況)可以分開考慮,答案爲每個事件概率的乘積。類似於積性函數中的一些理念。
  • n張牌的字母都是固定的,一般m個字符串的字母也是固定的(除非是兩個問號??)。
  • 考慮是否可以先分26個字母考慮,再考慮??。
  • rdp[j][k] 表示考慮到數字j用了k個問號的(不)合法情況數(合不合法,即是否存在重複計數的),把單個?的情況用組合數計數分配一下
  • 現在 ?? 還沒處理吧,所以??可以在這個層次上再套一層dp
  • val[i][w] 表示考慮到字母i已經用了w個??的合法方案數,最後揹包dp一遍求答案
  • (不確定有沒有爆 long long,感覺複雜度可以減掉一個60?
#include <bits/stdc++.h>
#define fi first
#define se second
#define o2(x) (x) * (x)
#define mk make_pair
#define eb emplace_back
#define SZ(x) ((int)(x).size())
#define all(x) (x).begin(), (x).end()
#define clr(a, b) memset((a), (b), sizeof((a)))
#define rep(i, s, t) for(register int i = (s), LIM=(t); i < LIM; ++i)
#define per(i, s, t) for(register int i = (s), LIM=(t); i > LIM; --i)
#define GKD std::ios::sync_with_stdio(false);cin.tie(0)
#define my_unique(x) sort(all(x)), x.erase(unique(all(x)), x.end())
using namespace std;
typedef long long LL;
typedef long long int64;
typedef unsigned long long uint64;
typedef pair<int, int> pii;
// mt19937 rng(time(NULL));//std::clock()
// mt19937_64 rng64(chrono::steady_clock::now().time_since_epoch().count());
// shuffle(arr, arr + n, rng64);
inline int64 read() {
    int64 x = 0;int las = 0;char ch = getchar();
    while (ch < '0' || ch > '9') las |= (ch == '-'), ch = getchar();
    while (ch >= '0' && ch <= '9') x = (x << 3) + (x << 1) + ch - '0', ch =
    getchar(); return x = las ? -x : x;
}
inline void write(int64 x, bool las = true) {
    if (x == 0) {putchar('0'); if(las)putchar('\n');else putchar(' ');return;}
    if (x < 0) {putchar('-');x = -x;}
    static char s[23];
    int l = 0;
    while (x != 0)s[l++] = x % 10 + 48, x /= 10;
    while (l)putchar(s[--l]);
    if(las)putchar('\n');else putchar(' ');
}
int lowbit(int x) { return x & (-x); }
template <class T>
T big(const T &a1, const T &a2) {return a1 > a2 ? a1 : a2;}
template <class T>
T sml(const T &a1, const T &a2) {return a1 < a2 ? a1 : a2;}
template <typename T, typename... R>
T big(const T &las, const R &... r) {return big(las, big(r...));}
template <typename T, typename... R>
T sml(const T &las, const R &... r) {return sml(las, sml(r...));}
void debug_out() { cout << '\n'; }
template <typename T, typename... R>
void debug_out(const T &las, const R &... r) {
    cout << las << " ";
    debug_out(r...);
}
#ifdef LH_LOCAL
#define debug(...) cout << "[" << #__VA_ARGS__ << "]: ", debug_out(__VA_ARGS__);
#else
#define debug(...) ;
#endif
#define debug(...) ;
/*================Header Template==============*/
const int mod = 998244353;// 998244353
int ksm(int a, int64 b, int kmod = mod) {int res = 1;for(;b > 0;b >>= 1, a = (int64)a * a % kmod) if(b &1) res = (int64)res * a % kmod;return res;}
const int INF = 0x3f3f3f3f;
const int MXN = 2e5 + 5;
const int MXM = 5e6 + 7;
int n, m;
char card[65][3], str[65][3];
// 0 <= . <= 9, A <= . <= Z  -> 1 <= . <= 10
int cnt[30][15], need[30][15];
int sum[2][30];
LL fdp[30], rdp[15][65], val[30][65], dp[30][65];

LL COMB(int n, int m) {
    if(n == m) return 1;
    if(n < m) return 0;
    LL res = 1;
    for(int i = 0; i < m; ++i) {
        res *= (n - i);
        res /= i + 1;
    }
    return res;
}

void work() {
    n = read(), m = read();
    for(int i = 1; i <= n; ++i) scanf("%s", card[i]);
    for(int i = 1; i <= m; ++i) scanf("%s", str[i]);
    int flag = 1;
    for(int i = 1; i <= n; ++i) {
        cnt[card[i][1] - 'A'][card[i][0] - '0' + 1] ++;
        sum[0][card[i][1] - 'A'] ++;
    }
    LL ww = m;
    for(int i = 1; i <= m; ++i) {
        if(str[i][1] != '?') {
            if(str[i][0] != '?') need[str[i][1] - 'A'][str[i][0] - '0' + 1] ++;
            else need[str[i][1] - 'A'][0] ++;
            sum[1][str[i][1] - 'A'] ++;
            ww --;
        }
    }
    LL res = 1;
    for(int i = 0; i < 26; ++i) {
        if(sum[0][i] < sum[1][i]) {
            flag = 0;
            break;
        }
        for(int w = 0; w <= ww; ++w) {
            need[i][0] += w;
            fdp[i] = 1;
            memset(rdp, 0, sizeof(rdp));
            rdp[0][0] = 1;
            // rdp[j][k] 表示考慮到數字j用了k個問號的(不)合法情況數(合不合法,即是否存在重複計數的)
            // 把單個?的情況用組合數計數分配一下
            // ?? 還沒處理吧,所以??可以在這個層次上再套一層dp
            // val[i][w] 表示考慮到字母i已經用了w個??的合法方案數
            for(int j = 1; j <= 10; ++j) {
                if(cnt[i][j] < need[i][j]) {
                    flag = 0;
                    break;
                }
                fdp[i] = fdp[i] * COMB(cnt[i][j], need[i][j]);
                // for(int k = 0; k <= need[i][0]; ++k) rdp[j][k] = rdp[j - 1][k];
                if(1 || cnt[i][j] > need[i][j] && need[i][j] > 0) {
                    for(int h = 0; h <= cnt[i][j] - need[i][j]; ++h) {
                        for(int k = h; k <= need[i][0]; ++k) {
                            rdp[j][k] = rdp[j][k] + rdp[j - 1][k - h] * COMB(cnt[i][j], need[i][j] + h);
                            // debug(h, j, k, rdp[j][k])
                        }
                    }
                    // if(i == 'C' - 'A') debug(j)
                }else {
                    // if(i == 'C' - 'A') debug(j, rdp[j][1], rdp[j - 1][1])
                }
            }
            // rdp[10][0] = 0;
            fdp[i] = fdp[i] * COMB(sum[0][i] - (sum[1][i] - need[i][0]), need[i][0]);
            LL ret = rdp[10][need[i][0]];
            if(i == 'C' - 'A') debug(i, w, need[i][0], fdp[i], rdp[10][need[i][0]], fdp[i] - rdp[10][need[i][0]], ret, fdp[i] - ret)
            res *= (ret);
            need[i][0] -= w;
            val[i][w] = ret;
            if(i == 'C' - 'A' && val[i][w] > 1) debug(i, w, val[i][w])
        }
    }
    for(int j = 0; j <= ww; ++j) dp[0][j] = val[0][j];
    for(int i = 1; i < 26; ++i) {
        for(int j = 0; j <= ww; ++j) {
            for(int k = 0; k <= j; ++k) {
                dp[i][j] = dp[i][j] + dp[i - 1][j - k] * val[i][k];
            }
        }
    }
    res = dp[25][ww];
    LL allcase = COMB(n, m);
    LL agcd = __gcd(allcase, res);
    debug(allcase, res, res / agcd, allcase / agcd)
    if(flag == 0) {
        printf("0/1\n");
    }else {
        printf("%lld/%lld\n", res / agcd, allcase / agcd);
    }
    // 不確定有沒有爆 long long
    for(int i = 0; i < 26; ++i) {
        sum[0][i] = sum[1][i] = 0;
        for(int j = 0; j <= 10; ++j) {
            cnt[i][j] = need[i][j] = 0;
        }
        for(int j = 0; j <= ww; ++j) {
            val[i][j] = 0;
            dp[i][j] = 0;
        }
    }
}

int main() {
#ifdef LH_LOCAL
    freopen("c:/lh/system-lab/acm_code/OJ/in.txt", "r", stdin);
    // freopen("c:/lh/system-lab/acm_code/OJ/out.txt", "w", stdout);
#endif
    for(int cas = 1, tim = (1 ? read(): 1); cas <= tim; ++ cas) {
        work();
    }
    // 56160000
    debug(26*60*10*60*60)
#ifdef LH_LOCAL
    cout << "Debug log: time cost:" << 1.0 * clock() / CLOCKS_PER_SEC << "s" << endl;
#endif
    return 0;
}
/*
3
40 2
0C 1C 2C 3C 4C 5C 6C 7C 8C 9C 0C 1C 2C 3C 4C 5C 6C 7C 8C 9C 0S 1S 2S 3S 4S 5S 6S 7S 8S 9S 0P 1P 2P 3P 4P 5P 6P 7P 8P 9P
?C 1C
40 16
0C 1C 2C 3C 4C 5C 6C 7C 8C 9C 0C 1C 2C 3C 4C 5C 6C 7C 8C 9C 0S 1S 2S 3S 4S 5S 6S 7S 8S 9S 0P 1P 2P 3P 4P 5P 6P 7P 8P 9P
0C 0C 1C 2C ?C ?C ?C ?C 3S 4S ?S ?S 5P 6P ?P ?P
60 30
1A 1B 1C 1D 1E 1F 1G 1H 1I 1J 1K 1L 1M 1N 1O 1P 1Q 1R 1S 1T 1U 1V 1W 1X 1Y 1Z 0A 1A 2A 3A 4A 5A 6A 7A 8A 9A 1S 3S 5S 7S 9S 0U 2U 4U 6U 8U 2Z 3Z 5Z 7Z 1C 1C 1C 1C 1C 1C 1C 1C 1S 1P
1C 1C 1S 1P 2A 0A 1A 9A ?S ?U ?Z ?H ?O ?U ?? ?? ?? ?? ?? ?? ?S ?S ?S ?U ?U ?U ?Z ?Z ?C ?C

37/780
167384/2417388525  -> 4351984/x -> 5551 * 28 * 28
50442363273/29566145391215356 -> 201769453092/x

Sample
Input
15
6 1
0C 0S 0P 1C 1S 1P
1S
6 1
0C 0S 0P 1C 1S 1P
?C
6 2
0C 0S 0P 1C 1S 1P
?C ?C
6 2
0C 0S 0P 1C 1S 1P
?C ?S
6 4
0C 0S 0P 1C 1S 1P
?C ?C ?S ?P
6 4
0C 0S 0P 1C 1S 1P
0C ?C ?S ?P
6 4
0C 0S 0P 1C 1S 1P
?C ?C ?S ??
6 4
0C 0S 0P 1C 1S 1P
?C ?C ?? ??
6 4
0C 0S 0P 1C 1S 1P
?A ?? ?? ??
6 4
0C 0S 0P 1C 1S 1P
0C 0C ?S ?P
30 8
0C 1C 2C 3C 4C 5C 6C 7C 8C 9C 0S 1S 2S 3S 4S 5S 6S 7S 8S 9S 0P 1P 2P 3P 4P 5P 6P 7P 8P 9P
0C ?C ?C ?C 1S ?S 2P ?P
40 2
0C 1C 2C 3C 4C 5C 6C 7C 8C 9C 0C 1C 2C 3C 4C 5C 6C 7C 8C 9C 0S 1S 2S 3S 4S 5S 6S 7S 8S 9S 0P 1P 2P 3P 4P 5P 6P 7P 8P 9P
?C 1C
40 2
0C 1C 2C 3C 4C 5C 6C 7C 8C 9C 0C 1C 2C 3C 4C 5C 6C 7C 8C 9C 0S 1S 2S 3S 4S 5S 6S 7S 8S 9S 0P 1P 2P 3P 4P 5P 6P 7P 8P 9P
?C 1S
40 16
0C 1C 2C 3C 4C 5C 6C 7C 8C 9C 0C 1C 2C 3C 4C 5C 6C 7C 8C 9C 0S 1S 2S 3S 4S 5S 6S 7S 8S 9S 0P 1P 2P 3P 4P 5P 6P 7P 8P 9P
0C 0C 1C 2C ?C ?C ?C ?C 3S 4S ?S ?S 5P 6P ?P ?P
60 30
1A 1B 1C 1D 1E 1F 1G 1H 1I 1J 1K 1L 1M 1N 1O 1P 1Q 1R 1S 1T 1U 1V 1W 1X 1Y 1Z 0A 1A 2A 3A 4A 5A 6A 7A 8A 9A 1S 3S 5S 7S 9S 0U 2U 4U 6U 8U 2Z 3Z 5Z 7Z 1C 1C 1C 1C 1C 1C 1C 1C 1S 1P
1C 1C 1S 1P 2A 0A 1A 9A ?S ?U ?Z ?H ?O ?U ?? ?? ?? ?? ?? ?? ?S ?S ?S ?U ?U ?U ?Z ?Z ?C ?C
Output
1/6
1/3
1/15
4/15
4/15
4/15
1/3
2/5
0/1
0/1
252/216775
37/780
1/39
167384/2417388525
50442363273/29566145391215356

Hint
對於分數 a/b,設 g=gcd(a,b),那麼其最簡分數形式爲 (a/g)/(b/g)。


*/
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章