zoukankan      html  css  js  c++  java
  • HDU-4532 湫秋系列故事——安排座位 组合数学DP

    题意:有来自n个专业的学生,每个专业分别有ai个同学,现在要将这些学生排成一行,使得相邻的两个学生来自不同的专业,问有多少种不同的安排方案。

    分析:首先将所有专业的学生视作一样的,最后再乘以各自学生的数量的阶乘。排列的时候通过动态规划来处理,设状态为前i个系,一共有j个位置相邻位置来自同系,然后转移。具体见代码注释。

    #include <cstdlib>
    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    using namespace std;
    
    typedef long long LL;
    const LL mod = (int)(1e9)+7;
    LL A[50];
    LL C[500][500]; 
    LL dp[50][500]; // dp[i][j]表示处理到第i组,一共还有j个位置左右坐的同学来自同一个专业 
    int n;
    int seq[50];
    
    void pre() {
        A[0] = A[1] = 1;
        for (int i = 2; i < 50; ++i) {
            A[i] = A[i-1] * i % mod;
        }
        for (int i = 0; i < 500; ++i) {
            C[0][i] = 1;
            for (int j = 1; j <= i; ++j) {
                C[j][i] = (C[j][i-1] + C[j-1][i-1]) % mod;
            }
        }
    }
    
    int solve() {
        memset(dp, 0, sizeof (dp));
        dp[1][seq[1]-1] = 1; // 给相邻同学来自一个系的间隙叫做粘着点 
        LL sum = seq[1];
        for (int i = 2; i <= n; ++i) {
            for (int j = 0; j < sum; ++j) { // sum表示处理到前i-1组最多有sum个粘着点 
                for (int k = 1; k <= seq[i]; ++k) { // 枚举第i组同学被拆分成k个块放入到队伍中 
                    for (int h = 0; h <= j && h <= k; ++h) {
                    // 枚举有h个块放到了前面的j个粘着点,即破坏了粘着点,但显然块内带来了新的粘着点 
                        dp[i][j-h+seq[i]-k] += dp[i-1][j]*C[h][j]%mod*C[k-h][sum+1-j]%mod*C[k-1][seq[i]-1]%mod;
                        // C[h][j]表示h个快插入了哪些粘着点
                        // C[k-h][sum-1-j]表示k-h个块插入了那些非粘着点,总间隙是sum+1个
                        // C[k][seq[i]-1]表示这seq[i]个同学是如何划分成k个块的 
                        dp[i][j-h+seq[i]-k] %= mod;
                    }
                }
            }
            sum += seq[i]; 
        }
        LL ret = dp[n][0];
        for (int i = 1; i <= n; ++i) {
            ret = ret * A[seq[i]] % mod;
        }
        return ret;
    }
    
    int main() {
        int T, ca = 0;
        pre();
        scanf("%d", &T);
        while (T--) {
            scanf("%d", &n);
            for (int i = 1; i <= n; ++i) {
                scanf("%d", &seq[i]);
            }
            printf("Case %d: %d
    ", ++ca, solve());
        }
        return 0;
    }
  • 相关阅读:
    建模:确定服务的边界——《微服务设计》读书笔记
    linux & windows下重启oracle
    Git配置用户名与邮箱
    Git中使用amend解决提交冲突
    微服务架构师的职责——《微服务设计读书笔记》
    MAC下配置ssh让SourceTree通过秘钥访问远程仓库
    微服务的概念——《微服务设计》读书笔记
    Uva 11572 唯一的雪花
    Codeforces Round #404 (Div. 2) ABC
    tyvj 1031 热浪 最短路
  • 原文地址:https://www.cnblogs.com/Lyush/p/3418646.html
Copyright © 2011-2022 走看看