D. Recovering BST 区间DP

D. Recovering BST 区间DP

题目大意:

给你一个不递减大小为n的一个序列,如果两个数的最大公约数大于1,那么这两个数可以建一条边,问最后是否可以构成一颗二叉搜索树。

题解:

这个题目写了很久,一直也没有想明白怎么写,不过可以发现的是这个是一个区间DP,然而因为这个数据范围让我又以为不是区间DP。

最后看了题解,非常巧妙的一个想法。

正常的来说,应该是定义:\(dp[i][j][k]\) 表示从 \(i\)\(j\)\(k\) 为根节点,但是这样的复杂度就太高了 \(O(n^5)\) ,考虑进行优化,因为这个是一个二叉搜索树,所以如果我知道了左子树是 \([i,j]\) 那么这个树的根节点应该是 \(i+1\) ,如果 \([i,j]\) 是右子树的话,那么这棵树的根节点应该是 \(i-1\) ,这个我觉得还是蛮难发现的,可能是我做的题目太少了。

可以思考一下这个转移方程:

  • 两棵子树拼接
  • 一棵树和一个节点拼接
#include <bits/stdc++.h>
#define inf 0x3f3f3f3f
#define inf64 0x3f3f3f3f3f3f3f3f
using namespace std;
const int maxn = 1e3+10;
typedef long long ll;
int a[maxn];
int dp[maxn][maxn][2],e[maxn][maxn];
int gcd(int a,int b){
    return b==0?a:gcd(b,a%b);
}
int main(){
    int n;
    scanf("%d",&n);
    for(int i=1;i<=n;i++) scanf("%d",&a[i]);
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
            e[i][j] = (gcd(a[i],a[j])!=1);
    for(int i=1;i<=n;i++){
        if(i>1&&e[i-1][i]) dp[i][i][1] = 1;//right
        if(i<n&&e[i+1][i]) dp[i][i][0] = 1;//left
    }
    for(int len=1;len<=n;len++){
        for(int i=1;i+len-1<=n;i++){
            int j = i + len - 1;
            if(dp[i][j][0]){
                for(int k=j+2;k<=n;k++){
                    if(dp[j+2][k][1]){
                        if(e[j+1][k+1]) dp[i][k][0] = 1;
                        if(e[j+1][i-1]) dp[i][k][1] = 1;
                    }
                }
                if(e[j+1][j+2]) dp[i][j+1][0] = 1;
                if(e[j+1][i-1]) dp[i][j+1][1] = 1;
            }
            if(dp[i][j][1]){
                for(int k=1;k<=i-2;k++){
                    if(dp[k][i-2][0]){
                        if(e[i-1][j+1]) dp[k][j][0] = 1;
                        if(e[i-1][k-1]) dp[k][j][1] = 1;
                    }
                }
                if(e[i-1][j+1]) dp[i-1][j][0] = 1;
                if(e[i-1][i-2]) dp[i-1][j][1] = 1;
            }
        }
    }
    dp[1][0][0] = 1;
    bool flag = dp[1][n-1][0];
    for(int i=2;i<=n;i++){
        if(dp[1][i-2][0]&&dp[i][n][1]) flag = true;
    }
    if(flag) printf("Yes\n");
    else printf("No\n");
    return 0;
}
posted @ 2021-01-31 20:19  EchoZQN  阅读(53)  评论(0编辑  收藏  举报