zoukankan      html  css  js  c++  java
  • 束缚二叉树 [树形dp]

    束缚二叉树



    color{red}{正解部分}

    一个子树内的 前序遍历序 是连续的,
    于是可以设 F[i,j]F[i, j] 表示以 ii 的子树, 子树末节点编号为 jj 时的方案数,
    :F[i,j]=k=ijF[i,k]×F[k+1,j]状态转移: F[i, j] = sumlimits_{k=i}^{j} F[i, k] imes F[k+1, j] .

    考虑怎么满足题目的限制条件, 设左子树的 中序遍历 编号为 aa, 右子树为 cc, 根节点为 bb,
    中序遍历 的要求是: a<ba < b, a<ca < c, b<cb < c .

    由于 前序遍历 编号是连续的, 所以可以考虑使用 二维矩阵前缀和 判断 转移是否合法 .

    具体来说:
    若存在限制 u,vu, v, 则令矩阵数组 a[u,v]=1a[u, v] = 1, 表示 中序遍历uu 的编号 要 小于 vv 的 编号,
    于是对于前面提到的三个限制, 只需要

    • 判断 左上角 (i,i+1)(i, i+1) 右下角 (i,k)(i, k) 的矩阵是否含有 11 .
    • 判断 左上角 (k+1,i+1)(k+1, i+1) 右下角 (j,k)(j, k) 的矩阵是否含有 11 .
    • 判断 左上角 (k+1,i)(k+1, i) 右下角 (j,i)(j, i) 的矩阵是否含有 11 .

    若上方有一个矩阵含有 11, 说明状态转移不合法, 不予以转移 .


    color{red}{实现部分}

    使用递归实现 .

    #include<bits/stdc++.h>
    #define reg register
    
    int read(){
            char c;
            int s = 0, flag = 1;
            while((c=getchar()) && !isdigit(c))
                    if(c == '-'){ flag = -1, c = getchar(); break ; }
            while(isdigit(c)) s = s*10 + c-'0', c = getchar();
            return s * flag;
    }
    
    const int maxn = 305;
    const int mod = 1e9 + 7;
    
    int N;
    int M;
    int a[maxn][maxn];
    int F[maxn][maxn];
    
    int calc(int x1, int y1, int x2, int y2){ return a[x2][y2] - a[x2][y1-1] - a[x1-1][y2] + a[x1-1][y1-1]; }
    
    int solve(int i, int j){
            if(i >= j) return 1;
            if(~F[i][j]) return F[i][j];
            int res = 0;
            for(reg int k = i; k <= j; k ++){
                    if(calc(i, i+1, i, k)) continue ;
                    if(calc(k+1, i, j, i)) continue ;
                    if(calc(k+1, i+1, j, k)) continue ;
                    res = (res + (1ll*solve(i+1, k)*solve(k+1, j)%mod)) % mod;
            }
            return F[i][j] = res;
    }
    
    void Work(){
            N = read(), M = read();
            memset(a, 0, sizeof a);
            memset(F, -1, sizeof F);
            for(reg int i = 1; i <= M; i ++){
                    int u = read(), v = read();
                    if(u == v){ printf("0
    "); return ; }
                    a[u][v] = 1;
            }
            for(reg int i = 1; i <= N; i ++)
                    for(reg int j = 1; j <= N; j ++) a[i][j] += a[i-1][j] + a[i][j-1] - a[i-1][j-1];
            printf("%d
    ", solve(1, N));
    }
    
    int main(){
            int T = read();
            while(T --) Work();
            return 0;
    }
    
  • 相关阅读:
    C#中char[]与string之间的转换
    Java文件操作之文件追加 Chars
    冒泡排序法的改进 Chars
    选择排序 Chars
    编译原理 Chars
    Apk反编译 Chars
    VC中常用的方法 Chars
    Node.Js入门级《培训》
    新概念系列之《Part2 Lesson 24 It could be worse》
    《CLR via C#》Part2之Chapter5 基元类型、引用类型和值类型(三)
  • 原文地址:https://www.cnblogs.com/zbr162/p/11822450.html
Copyright © 2011-2022 走看看