• Codeforces 1152D DP


    题意:有一颗由长度为2 * n的合法的括号序列构成的字典树,现在你需要在这颗字典树上选择一些不连接的边,问最多可以选择多少条边?

    思路:不考虑题目条件的话,我们只考虑在随意的一棵树上选择边,这是一个贪心的问题,只需要每次选择叶子结点和它的父亲这条边,然后把它和它父亲节点删除就可以了,不断进行这个操作,就可以得到最终的答案。但是题目中的这颗字典树的规模是非常大的,不能用正常的方法。我们发现这颗字典树有2个性质:

    1:这颗字典树的所有叶子结点都在同一层。

    2:有很多的子树是重复的。

    通过第一个性质以及贪心算法,我们知道答案相当于是计算奇数层的点的个数(如果根按第0层开始算),从第2n层开始选择,因为2n层节点个数一定大于等于第2n - 1层,所以可以选择的最多的边数是2n - 1层的节点数,2n - 2层同理。

    那么问题就转化为的求这颗字典树的奇数层的节点的个数了。

    现在我们需要用到第二个性质,在这颗字典树中,容易发现,有些树的转移是相同的,我们假设用i(当前填充的括号数)和j(平衡因子)来表示一个状态,那么它只可能转移到dp[i + 1][j + 1]或者dp[i + 1][j - 1], 对应的转移是加一个右括号和加一个左括号,而i同时还等于深度,所以我可以用dp[i][j]表示深度为i,平衡因子是j的节点的个数,这样就可以O(n ^ 2)转移了。

    代码:

    #include <bits/stdc++.h>
    #define LL long long
    using namespace std;
    const LL mod = 1000000007;
    const int maxn = 2010;
    int dp[maxn][maxn]; 
    int main() {
    	int n;
    	scanf("%d", &n);
    	dp[0][0] = 1;
    	LL ans = 0;
    	for (int i = 1; i <= 2 * n; i++) {
    		for (int j = 0; j <= i; j++) {
    			if(j > 0) dp[i][j] = (dp[i][j] + dp[i - 1][j - 1]) % mod;
    			dp[i][j] = (dp[i][j] + dp[i - 1][j + 1]) % mod;
    			if(i % 2 == 1 && (i - j) % 2 == 0 && 2 * n - i >= j)
    				ans = (ans + dp[i][j]) % mod;
    		}
    	}
    	printf("%lld
    ", ans);
    }
    

      

  • 相关阅读:
    软件测试:Homework 3
    软件测试:Lab 1
    软件测试:Homework 2
    软件测试:Homework 1
    JAVA的回忆
    Java 操作符
    Java 笔录
    经典C#编程理解,概要,经典
    网络精灵
    签到计时
  • 原文地址:https://www.cnblogs.com/pkgunboat/p/10829873.html
Copyright © 2020-2023  润新知