zoukankan      html  css  js  c++  java
  • [CF1025D] Recovering BST(区间DP入门)

    前言

    作为一名DP矮子,就从这道看似简单的区间DP开始学习区间DP吧

    以下文字纯属个人理解

    让人觉得很淦的提示:CF是少爷机

    题目

    洛谷

    CF

    讲解

    何为区间DP?

    DP的预处理即为求出小区间的状态

    我们通过小区间更新次小区间,最后到大区间

    完成最后对答案的计算

    此为区间DP

    区间DP的特征?

    首先它要长得像个DP (完全凭感觉),比如数据范围比较小之类的

    就目前我遇到的区间DP而言两个小区间能够满足合成为大区间的性质(通常时间复杂度要比状态数高一阶)

    当然也可以是一个区间向旁边扩展一个格子(通常时间复杂度即为状态数)

    大致做法

    首先我们要把DP状态定义出来(难道这不是每个DP的基操?)

    先更新小区间!!!

    正如我们之前提到的,我们一定要先求出小区间的解

    不然你怎么更新大区间?

    本题思路

    由于这是一棵二叉搜索树

    所以我们只需将左右儿子合并成一棵树,作为另一个根的某个儿子

    长得就是一副区间DP的样子

    确定了方法后,我们来尝试定义状态

    由于我们有左右两个儿子,我们使用两个DP数组:

    [egin{cases} L[l][r]:区间[l,r-1]作为左儿子,连向根r的状态\ R[l][r]:区间[l+1,r]作为右儿子,连向根l的状态\ end{cases}]

    我们按部就班,先更新最小区间

    在这里,我们发现DP边界即为(l=r)的情况

    [egin{cases} L[l][r] = 1 (l = r)\ R[l][r] = 1 (l = r)\ end{cases}]

    然后我们可以预处理出两个数能否连边,使用(pd[i][j])数组存储,时间复杂度为(O(n^2logn))

    最后是DP转移,即将两个小区间拼起来

    考虑枚举区间[l,r]的根(i (l<=i<=r))

    如果满足拼起来的条件(if(L[l][i] && R[i][r]))

    试图更新更大的区间,考虑向左或右扩展:

    [egin{cases} if(pd[i][r+1]) L[l][r+1] = 1\ if(pd[l-1][i]) R[l-1][r] = 1\ end{cases}]

    如果拼起来之后的区间为[1,n]了,即有解,输出(Yes)即可

    反之,如果到最后也没有跑出有解,输出(No)

    实在没懂看看代码也就懂了

    代码

    //12252024832524
    #include <cstdio>
    #include <algorithm>
    using namespace std; 
    
    typedef long long LL;
    const int MAXN = 705;
    int n;
    int a[MAXN];
    bool pd[MAXN][MAXN],L[MAXN][MAXN],R[MAXN][MAXN];
    
    int gcd(int x,int y)
    {
    	if(!y) return x;
    	return gcd(y,x%y);
    }
    
    int main()
    {
    //	freopen(".in","r",stdin);
    //	freopen(".out","w",stdout);
    	n = Read();
    	for(int i = 1;i <= n;++ i) a[i] = Read(),L[i][i] = R[i][i] = 1;
    	for(int i = 1;i <= n;++ i)
    		for(int j = i+1;j <= n;++ j)
    			pd[i][j] = pd[j][i] = (bool)(gcd(a[i],a[j])-1);
    	for(int l = n;l >= 1;-- l)//细品,从小到大枚举区间
    		for(int r = l;r <= n;++ r)
    			for(int i = l;i <= r;++ i)//枚举根 
    			{
    				if(L[l][i] && R[i][r])
    				{
    					if(l == 1 && r == n) {printf("Yes");return 0;}
    					if(pd[i][r+1]) L[l][r+1] = 1;
    					if(pd[l-1][i]) R[l-1][r] = 1;
    				}
    			}
    	printf("No");
    	return 0;
    }
    
  • 相关阅读:
    返回一个整数数组中最大子数组的和(数组头尾连接)
    场景调研(补)
    《浪潮之巅》读书笔记3
    《软件工程》课程改进意见
    【每日scrum】第一次冲刺day6
    【每日scrum】第一次冲刺day5
    【每日scrum】第一次冲刺day4
    【每日scrum】第一次冲刺day3
    【每日scrum】第一次冲刺day2
    【每日scrum】第一次冲刺day1
  • 原文地址:https://www.cnblogs.com/PPLPPL/p/13393332.html
Copyright © 2011-2022 走看看