dp题目的优化

前言

  今天做最长连续自序列的时候,发现有的时候dp:O(n2)的复杂度会超时,然而我们可以通过优化把时间复杂度降低为O(nlog2n),这个过程主要用到了二分和贪心的思想。优化过程如下:参考博客

  dp[i]表示长度为i的上升子序列的最后一个元素的最小值。 然后考虑如何转移:
 
  现在我们有一个新元素a,对比dp的最后一个元素。如果a>dp[len] ,那么可以直接把a“接到”dp[len]的后面, 形成更长的上升子序列。即:dp[++len]=a;
  否则,找到dp中第一个大于等于a的元素,用a替换它 。这样替换后既保证仍形成上升子序列,又使得该上升子序列的最后元素更小。
  但是这样做,无法记录路径。
 

例题

P1020 [NOIP1999 普及组] 导弹拦截

  本题只需要找到最长非递增子序列和最长递增子序列即可,其中用到了lower_bound 和 upper_bound函数二分查找。这两个函数只能在升序序列中使用,所以在递减序列中查找时,需要修改一个小细节upper_bound(dp + 1, dp + sum + 1, a[i], greater<int>()) - dp 即可。
 
复制代码
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e5 + 20;
const int inf = 0x3f3f3f3f;

int a[maxn];
int dp[maxn];
int cnt = 0, tot = 0, sum = 0;
//O(nlogn)
int main() {
    int num;

    while (~scanf("%d", &num)) a[++ cnt] = num;
    //最长非递增子序列
    memset(dp, inf, sizeof(dp));
    for (int i = 1; i <= cnt; i ++) {
        if (a[i] <= dp[sum]) {
            dp[++ sum] = a[i];
        } else {
            //只有在升序中才可以使用lower_bound 和 upper_bound
            int t = upper_bound(dp + 1, dp + sum + 1, a[i], greater<int>()) - dp;
            dp[t] = a[i];
        }
    }
    cout << sum << "\n";
    //最长上升子序列
    memset(dp, 0, sizeof(dp));
    for (int i = 1; i <= cnt; i ++) {
        if (a[i] > dp[tot]) dp[++ tot] = a[i];
        else {
            int t = lower_bound(dp + 1, dp + 1 + tot, a[i]) - dp;
            dp[t] = a[i];
        }
    }
    cout << tot << "\n";

    return 0;
}
复制代码

 

 

#504. 连续子序列

  最长上升子序列的翻版,多了一个要求,就是子序列中的数字要连续,即相邻两个数的差值为1。由于子序列要求是连续的,所以每一个状态只能由比当前数小1的状态得来,根本不用暴搜,只要询问前一个值即可。a的范围是1e9,直接开数组空间会炸,用一个map就可以解决了。  参考博客

 

复制代码
#include<bits/stdc++.h>
using namespace std;

#define ll long long
#define TLE std::ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
const int maxn = 2e5 + 10;
int a[maxn];
map<int, int> f;

int main() {
    TLE;
    int n;
    cin >> n;
    for (int i = 1; i <= n; i ++) {
        cin >> a[i];
    } 
    int maxx = 0, flag ;
    for(int i = 1; i <= n; i ++) {
        f[a[i]] = f[a[i] - 1] + 1;
        if(f[a[i]] > maxx) {
            maxx = f[a[i]];
            flag = a[i];
        }
        else if(f[a[i]] == maxx && a[i] < flag) flag = a[i];
    }    
    cout << maxx << "\n";
    for(int i = flag - maxx + 1; i <= flag; i ++) {
        cout << i << " ";
    }
    
    cout << "\n";
    
    return 0;
}
复制代码

 

 

 

posted @   Y2ZH  阅读(34)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示