• 【算法竞赛进阶指南】金字塔(区间DP+dfs序)


    原题链接

    题意:

    给定序列表示dfs一棵树遍历得到的顺序,每次经过一个节点都输出该节点对应的字母,求有多少棵树满足此序列。

    思路:

    首先根据dfs的过程可以得到如果一棵树有n个节点的话,他的序列长度为2n-1;所以如果给出的长度为m的话,节点数量n=(m+1)/2。所以如果说给出的序列长度是偶数的话,答案一定为0;即每个子树的dfs序列长度必定为奇数。

    因为一段dfs序列可以对应一棵子树,考虑动态规划。

    dp[l] [r] 表示所有dfs序列是s[l~r]的树的个数,划分依据为最后一棵子树的范围,即枚举最后一棵子树dfs序列的起点k,区间[k,r]表示这棵子树。根据上文我们可以知道该段区间的长度必定为奇数,所以k=l,l+2……r-2.因为必须要是子树,r-1只有两个节点,都是根节点,不是子树,所以枚举到r-2。

    再来考虑状态计算。根据乘法原理,dp[l] [r]=l到k的序列构成的方案数×最后一棵子树的种类。对于后者,把根节点去掉后,又可以变成[k+1,r-1]的序列构成的方案数,这样就可以不断计算下去。

    最后,并不是所有的状态都是合法的,因为是dfs,所以起点和终点一定是相同的,也就是说l,r,k的字符串的值都是相同的。

    代码:

    #include<bits/stdc++.h>
    using namespace std;
    char s[310];
    typedef long long ll;
    ll dp[310][310];
    const int mod=1e9;
    int main(){
        cin>>s+1;
        int n=strlen(s+1);
        if(n%2==0){
            puts("0");
            return 0;
        }
        for(int len=1;len<=n;len+=2){
            for(int l=1;l+len-1<=n;l++){
                int r=l+len-1;
                if(len==1) dp[l][r]=1;
                else{
                    if(s[l]==s[r]){
                        for(int k=l;k<=r-2;k+=2){
                            if(s[l]==s[k]){
                                dp[l][r]=(dp[l][r]+dp[l][k]*dp[k+1][r-1])%mod;
                            }
                        }
                    }
                }
            }
        }
        printf("%lld
    ",dp[1][n]);
        return 0;
    }
    

    参考文献

  • 相关阅读:
    Ubuntu安装最新版的nodejs
    Mac安装并破解OmniGraffle7
    Mac安装并破解StarUML
    Windows10使用Chocolatey安装mysql之后无法使用的解决办法
    Visual Studio编辑类模板的位置
    VS2017连接到中国区的Azure
    Windows上包管理器之Chocolatey初体验
    CENTOS7.3 64位架设使用MYSQL数据库的ASP.NET CORE网站
    从无到有开发自己的Wordpress博客主题---主页模板
    c# 获得方法的所属类(或调用者)的类名,方法名
  • 原文地址:https://www.cnblogs.com/OvOq/p/14853102.html
Copyright © 2020-2023  润新知