• 计蒜客 奇异家庭 (DP)



    **链接 : ** Here!

    思路 :

    1. 首先这棵家族树非常非常非常有特点, 家族里的人要么没有孩子, 要么有两个孩子, 所以这棵家族树是一颗满二叉树.

    2. 设定状态 $dp[i][j]$ 为 $i$ 个人组成的不超过 $j$ 层的家谱结构种数, 首先明确一点, 那些状态会为这个状态贡献值 ? 自然能够想到左右两个孩子, 也就是 $dp[m][j-1]$, $dp[i-1-m][j-1]$ $(1 leq m leq i-2)$ , 那么很自然的就能够得到状态转移方程 :
      1. $dp[1][j] = 1, (1 leq j leq K)$
      2. $dp[i][j] = prod_{m = 1}^{i - 2} {(dp[m][j - 1] * dp[i - 1 - m][j - 1])}, (m eq 偶数, 2 leq i leq N, i eq 偶数, 2 leq j leq K)$

    3. 为什么上面状态转移方程中 $i$ 和 $m$ 都必须为奇数呢, 因为家谱树是一棵满二叉树, 所以节点数量只可能是奇数.

    补充 :

    1. 满二叉树:树中除了叶子节点,每个节点都有两个子节点
    2. 完全二叉树:在满足满二叉树的性质后,最后一层的叶子节点均需在最左边
    3. 完美二叉树:满足完全二叉树性质,树的叶子节点均在最后一层(也就是形成了一个完美的三角形)
    4. 一定要和国内的二叉树分类相区别!!!

    **代码 : **

    /*************************************************************************
    	> File Name: 奇异家庭.cpp
    	> Author: 
    	> Mail: 
    	> Created Time: 2017年11月21日 星期二 00时14分38秒
     ************************************************************************/
    
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    using namespace std;
    
    // 状态转移方程为dp[i][j] : i个人数组成的不超过j层的家谱结构数
    // 假设左子树的节点数量为m, 则右子树的节点数量为i - 1 - m, 注意左右子树节点也一定得是奇数
    // 因此dp[i][j] = SIGMA(dp[m][j - 1] * dp[i - 1 - m][j - 1]) (0 < m < i - 1)
    const int MAX_N = 200 + 10;
    const int MAX_K = 100 + 10;
    const int MOD = 9901;
    int dp[MAX_N][MAX_K];
    int n, k;
    
    void solve() {
        memset(dp, 0, sizeof(dp));
        for (int j = 1 ; j <= k ; ++j) {
            dp[1][j] = 1;
        }
        for (int j = 2 ; j <= k ; ++j) {
            // 注意奇数的情况下是不存在解的
            for (int i = 3 ; i <= n ; i += 2) {
                int sum = 0;
                for (int m = 1 ; m < i - 1 ; m += 2) {
                    sum = (sum + (dp[m][j - 1] * dp[i - 1 - m][j - 1])) % MOD;
                }
                dp[i][j] = sum % MOD;
            }
        }
        // 注意, 如果不取模的话dp[n][k] >= dp[n][k - 1]
        // 但是如果有了取模运算,dp[n][k]就有可能小于dp[n][k - 1]了
        printf("%d
    ", (dp[n][k] - dp[n][k - 1] + MOD) % MOD);
    }
    int main() {
        scanf("%d%d", &n, &k);
        if (!(n & 1))   printf("0
    ");
        else            solve();
        return 0;
    }
    
  • 相关阅读:
    vue插件(还真是第一次接触)
    Vue父组件向子组件传值以及data和props的区别
    Vue v-bind与v-model的区别
    vue 异步渲染
    vue动态加载不同的组件(分内部和外部组件)
    vue自定义组件的递归
    作用域插槽模板迭代的次数,取决于组件内部独立slot的数量
    说说 Vue.js 中的 v-cloak 指令
    C语言 system
    C语言 有符号、无符号
  • 原文地址:https://www.cnblogs.com/WArobot/p/7885079.html
Copyright © 2020-2023  润新知