码迷,mamicode.com
首页 > 其他好文 > 详细

UVALive 7143 Room Assignment(组合数学+DP)

时间:2017-08-11 20:27:00      阅读:126      评论:0      收藏:0      [点我收藏+]

标签:type   pac   for   blank   memset   upd   size   href   ref   

题目链接

参考自:http://www.cnblogs.com/oyking/p/4508260.html

题意

n个人,其中有k对双胞胎.现有m间房间,每间房间有容量ci问分配房间的方案数。

 

分析

设dp[i][j]为已经放满了第i个房间之后,所剩下的双胞胎的对数还有j对,然后对于i+1间房,我们可以从剩余的j对中选择出a对,每对双胞胎只是放一个就好,然后又从j-a对双胞胎中选b对全部放进去,然后再从剩余的sum-j2中选择c[i]-a-2b个放进i+1个房间里面,这样的话就能得到转移方程,dp[i+1][j-a-b]=dp[i][j]C(j,a)C(j-a,b)C(sum-j2,c[i]-a-b2);然后组合数就用逆元去算就ok了。时间复杂度是O((mk)³)

 

#include <cstdio>
#include <algorithm>
#include <iostream>
#include <cstring>
using namespace std;
typedef long long LL;

const int MAXN = 100010;
const int MOD = 1e9 + 7;

int inv[MAXN], fact[MAXN];

int _inv(int x) {
    if(x == 1) return 1;
    return LL(MOD - MOD / x) * _inv(MOD % x) % MOD;
}

void init(int n = 100000) {
    fact[0] = 1;
    for(int i = 1; i <= n; ++i)
        fact[i] = fact[i - 1] * LL(i) % MOD;
    for(int i = 0; i <= n; ++i)
        inv[i] = _inv(fact[i]);
}

LL comb(int a, int b) {
    if(a < b) return 0;
    return LL(fact[a]) * inv[b] % MOD * LL(inv[a - b]) % MOD;
}

int dp[13][111];
int c[13];
int T, n, m, k;

int mulmul(LL a, LL b, LL c, LL d) {
    return a * b % MOD * c % MOD * d % MOD;
}

void update_add(int &a, int b) {
    a += b;
    if(a >= MOD) a -= MOD;
}

int solve() {
    memset(dp, 0, sizeof(dp));
    dp[0][k] = 1;
    for(int i = 0, sum = n; i < m; ++i) {
        for(int r = 0; r <= k; ++r) if(dp[i][r]) {
            if(sum < 2 * r) break;
            for(int a = 0; a <= r; ++a) {
                for(int b = 0; b + a <= r; ++b) {
                    if(c[i + 1] - a - 2 * b < 0) break;
                    int t = mulmul(dp[i][r], comb(r, a), comb(r - a, b), comb(sum - 2 * r, c[i + 1] - a - 2 * b));
                    update_add(dp[i + 1][r - a - b], t);
                }
                if(i == m - 1) break; /// if i = m - 1 then a must be zero
            }
        }
        sum -= c[i + 1];
    }
    return dp[m][0];
}

int main() {
    init();
    //printf("%d\n", comb(10, 1));
    scanf("%d", &T);
    for(int t = 1; t <= T; ++t) {
        scanf("%d%d%d", &n, &m, &k);
        for(int i = 1; i <= m; ++i) scanf("%d", &c[i]);
        printf("Case #%d: %d\n", t, solve());
    }
}

 

UVALive 7143 Room Assignment(组合数学+DP)

标签:type   pac   for   blank   memset   upd   size   href   ref   

原文地址:http://www.cnblogs.com/fht-litost/p/7347759.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!