【区间DP && bitset && 内存】Codeforces Round #505 D. Recovering BST

Step1 Problem:

给你 n 个点,升序给出 n 个点的权值,让你判断是否能构成一棵排序树:两个节点权值的 gcd 不等于 1 才能够连边。
数据范围:
2 <= n <= 700, 2 <= a[i] <= 1e9.

Step2 Ideas:

扩充知识:通常情况下,1字节 = 8位. C++ 里面 int 占 4字节,char 占 1字节,bool 占 1字节,bitset 占 1位。
dp[L][R][fa]:子树 L 到 R, 父亲是 fa 是否能构成满足条件的排序树。
int:700*700*700 = 343000000
bool:700*700*700/4 = 85750000 (相当于 int 数组大小)
bitset:700*700*700 / 32 = 10718750(相当于 int 数组大小)
用 bitset,就不会被卡内存了
另外一种思路可以把内存降下来:
dp[L][R][2]:dp[L][R][0], 代表子树 L 到 R, L 为父亲;dp[L][R][1], 代表子树 L 到 R, R 为父亲。

Step3 Code:

#include<bits/stdc++.h>
using namespace std;
const int N = 705;
bitset<710> vis[N][N], dp[N][N];
int a[N], g[N][N];
int dfs(int l, int r, int fa)
{
    if(l > r) return 1;//空树返回 1
    if(vis[l][r][fa]) return dp[l][r][fa];// 记忆化
    vis[l][r][fa] = 1;// 标记这个状态已经走过
    if(l == r) {//根节点 直接判断
        if(g[l][fa] != 1) return 1;
        else return 0;
    }
    for(int root = l; root <= r; root++)// 枚举区间 l, r 的根
    {
        if(g[root][fa] != 1) {
            if(dfs(l, root-1, root) && dfs(root+1, r, root)) return dp[l][r][fa] = 1;
        }
    }
    return dp[l][r][fa] = 0;
}
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++)//预处理出两个点的 gcd
    {
        for(int j = 1; j <= n; j++)
            g[i][j] = __gcd(a[i], a[j]);
    }
    memset(vis, 0, sizeof(vis));
    memset(dp, 0, sizeof(dp));
    if(dfs(1, n, 0)) printf("Yes\n");
    else printf("No\n");
    return 0;
}
#include<bits/stdc++.h>
using namespace std;
const int N = 705;
int dp[N][N][2];// 0 代表 L 为父亲,1 代表 R 为父亲
int a[N], g[N][N];
int dfs(int l, int r, int fa)
{
    if(l == r) return 1;
   // printf("%d %d %d\n", l, r, fa);
    if(dp[l][r][fa] != -1) return dp[l][r][fa];
    if(r-l == 1) {
        if(g[l][r] != 1) return 1;
        else return 0;
    }
    for(int root = l; root <= r; root++)
    {
        if(!fa && root != l) {
            if(g[root][l] != 1) {
                if(dfs(l+1, root, 1) && dfs(root, r, 0)) return dp[l][r][fa] = 1;
            }
        }
        if(fa && root != r) {
            if(g[root][r] != 1) {
                if(dfs(l, root, 1) && dfs(root, r-1, 0)) return dp[l][r][fa] = 1;
            }
        }
    }
    return dp[l][r][fa] = 0;
}
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++)
            g[i][j] = __gcd(a[i], a[j]);
    }
    memset(dp, -1, sizeof(dp));
    for(int root = 1; root <= n; root++)
    {
        if(root == 1) {
            if(dfs(root, n, 0)) {
                printf("Yes\n");
                return 0;
            }
        }
        else if(root == n) {
            if(dfs(1, root, 1)) {
                printf("Yes\n");
                return 0;
            }
        }
        else if(dfs(1, root, 1) && dfs(root, n, 0)) {
            printf("Yes\n");
            return 0;
        }
    }
    printf("No\n");
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值