• UVALive 7143 Room Assignment(组合数学+DP)(2014 Asia Shanghai Regional Contest)


    题目链接:https://icpcarchive.ecs.baylor.edu/index.php?option=com_onlinejudge&Itemid=8&category=648&page=show_problem&problem=5155

    There are N guests checking in at the front desk of the hotel. 2K (0 ≤ 2K ≤ N) of them are twins.
    There are M rooms available. Each room has capacity ci which means how many guests it can hold.
    It happens that the total room capacity is N, i.e. c1 + c2 + . . . + cM = N.
    The hotel receptionist wonders how many different room assignments to accommodate all guests.
    Since the, receptionist cannot tell the two twins in any pair of twins apart, two room assignments are
    considered the same if one can be generated from the other by swapping the two twins in each of some
    number of pairs. For rooms with capacity greater than 1, it only matters which people are in the room;
    they are not considered to be in any particular order within the room.
    Input
    The first line of the input gives the number of test cases, T. T test cases follow. Each test case starts
    with three integers N, M, and K, which indicates the number of guests, the number of rooms, and the
    number of pairs of twins. The following line contains M integers, c1, c2, …, cM, which indicates the i-th
    room’s capacity.
    Output
    For each test case, first output one line containing ‘Case #x: y’, where x is the test case number
    (starting from 1) and y is the number of different room assignments modulo 1,000,000,007 (109 + 7).

    题目大意:有n个颜色的球,其中有k对球颜色相同,别的都是完全不同的。给m个盒子,每个盒子的容量为c[i],有sum{c[i]}=n。问:有多少种姿势可以把n个球全部放入盒子中。

    思路:首先这是一条组合计数的动态规划。

    用dp[i][r]表示,前i个盒子已经放完了,手上还拿着r对同色球。

    设对于状态dp[i][r],在第 i+1 个盒子中,我们要从 r 对同色球中取出 a 对,拿其中一个放入盒子 i+1 ;从剩下的 r-a 对同色球中,拿出 b 对,全部放入盒子 i+1 中;再从其他剩下的未放入盒子的球里面(假设有 sum 个),取 c[i]-a-2*b 个放入睇 i+1 个盒子中。这样便转移到了状态dp[i+1][r-a-b]。

    状态转移方程为:dp[i+1][r-a-b] = dp[i][r] * comb(r, a) * comb(r - a, b) * comb(sum - 2 * r, c[i] - a - 2 * b).

    其中comb(p, q)表示从 p 个物体中选出 q 个的组合数。

    至于在同色球中只选出其中一个球的问题,可以考虑:给两个球强行编号为1、2,然后强行要求1必需放在2的前面,这样就不会产生重复。当我们放入球1之后,球2就与其他普通的球无异了,无需任何处理。

    然后预处理一下阶乘及其逆元,利用公式comb(p, q) = p! / q! / (p-q)!,在O(1)时间内求出组合数。

    总的时间复杂度为O(mk^3)。

    代码(1.252S):

     1 #include <cstdio>
     2 #include <algorithm>
     3 #include <iostream>
     4 #include <cstring>
     5 using namespace std;
     6 typedef long long LL;
     7 
     8 const int MAXN = 100010;
     9 const int MOD = 1e9 + 7;
    10 
    11 int inv[MAXN], fact[MAXN];
    12 
    13 int _inv(int x) {
    14     if(x == 1) return 1;
    15     return LL(MOD - MOD / x) * _inv(MOD % x) % MOD;
    16 }
    17 
    18 void init(int n = 100000) {
    19     fact[0] = 1;
    20     for(int i = 1; i <= n; ++i)
    21         fact[i] = fact[i - 1] * LL(i) % MOD;
    22     for(int i = 0; i <= n; ++i)
    23         inv[i] = _inv(fact[i]);
    24 }
    25 
    26 LL comb(int a, int b) {
    27     if(a < b) return 0;
    28     return LL(fact[a]) * inv[b] % MOD * LL(inv[a - b]) % MOD;
    29 }
    30 
    31 int dp[13][111];
    32 int c[13];
    33 int T, n, m, k;
    34 
    35 int mulmul(LL a, LL b, LL c, LL d) {
    36     return a * b % MOD * c % MOD * d % MOD;
    37 }
    38 
    39 void update_add(int &a, int b) {
    40     a += b;
    41     if(a >= MOD) a -= MOD;
    42 }
    43 
    44 int solve() {
    45     memset(dp, 0, sizeof(dp));
    46     dp[0][k] = 1;
    47     for(int i = 0, sum = n; i < m; ++i) {
    48         for(int r = 0; r <= k; ++r) if(dp[i][r]) {
    49             if(sum < 2 * r) break;
    50             for(int a = 0; a <= r; ++a) {
    51                 for(int b = 0; b + a <= r; ++b) {
    52                     if(c[i + 1] - a - 2 * b < 0) break;
    53                     int t = mulmul(dp[i][r], comb(r, a), comb(r - a, b), comb(sum - 2 * r, c[i + 1] - a - 2 * b));
    54                     update_add(dp[i + 1][r - a - b], t);
    55                 }
    56                 if(i == m - 1) break; /// if i = m - 1 then a must be zero
    57             }
    58         }
    59         sum -= c[i + 1];
    60     }
    61     return dp[m][0];
    62 }
    63 
    64 int main() {
    65     init();
    66     //printf("%d
    ", comb(10, 1));
    67     scanf("%d", &T);
    68     for(int t = 1; t <= T; ++t) {
    69         scanf("%d%d%d", &n, &m, &k);
    70         for(int i = 1; i <= m; ++i) scanf("%d", &c[i]);
    71         printf("Case #%d: %d
    ", t, solve());
    72     }
    73 }
    View Code
  • 相关阅读:
    终于开通了
    <input>表单元素readonly时光标仍然可见
    关于字体
    SSI架构中get***方法潜在调用
    为uploads文件夹瘦身
    在JSP里使用CKEditor和CKFinder
    centos5.5上搭建svn服务器
    多文件上传
    属性化ATL,DCOM,SIM,IID
    BSTR转换成char*
  • 原文地址:https://www.cnblogs.com/oyking/p/4508260.html
Copyright © 2020-2023  润新知