• 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;
    }
  • 相关阅读:
    Ubuntu mongodb 安装和配置
    最基本的SQL语法/语句
    Sphinx学习之sphinx的安装篇
    六关节机器人的雅可比矩阵及微分运算
    六关节机器人的逆运动学计算
    六关节机器人的正运动学计算
    六关节机器人末端的微分运动
    Python3 升级pip
    一般多项式曲线的最小二乘回归(Linear Regression)
    关于卡尔曼滤波(Kalman Filter)的很好讲解
  • 原文地址:https://www.cnblogs.com/Lyush/p/3418646.html
Copyright © 2020-2023  润新知