首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> C++ >> C++ 入門

POJ 3670 & 3671:最長不下降子序列 (LNDS) 求最小修改次數

     編輯:C++ 入門

問題描述

給定長度 N ≤ 30000 的整數序列。
POJ 3670:可將序列修改為非遞減或非遞增,求最少修改元素個數。
POJ 3671:只能修改為非遞減,求最少修改個數。

核心等價轉化

最少修改次數 = N - 最長不下降子序列長度 (LNDS)。
保留 LNDS 的元素不動,其餘修改為適當值即可構成非遞減序列。
POJ 3670 額外計算最長不上升子序列 (LNIS),取兩者最大值。

O(N log N) LNDS 算法(二分 + 貪心維護尾值陣列)

tail[len] = 長度為 len 的不下降子序列的最小結尾值。
遍歷序列 x:

  • 若 x ≥ tail[len]:tail[++len] = x
  • 否則:二分查找 tail 中 第一個 > x 的位置 pos,tail[pos] = x

最終 len 即為 LNDS 長度。

POJ 3670 代碼(雙向)

#include 
#include 
#include 
using namespace std;
const int MAXN = 30010;
int a[MAXN], tail[MAXN];

int find_pos(int x, int len) { // 找第一個 > x 的位置
    int l=1, r=len, pos=len+1;
    while (l<=r) {
        int mid=(l+r)/2;
        if (tail[mid] > x) { pos=mid; r=mid-1; }
        else l=mid+1;
    }
    return pos;
}

int LNDS(int n) {
    int len=0;
    for (int i=0; i= tail[len]) tail[++len]=a[i];
        else tail[find_pos(a[i], len)] = a[i];
    }
    return len;
}

int main() {
    int n;
    while (scanf("%d", &n)==1) {
        for (int i=0; i

POJ 3671 代碼(單向)

#include 
#include 
#include 
using namespace std;
const int MAXN = 30010;
int tail[MAXN];

int find_pos(int x, int len) { ... } // 同上

int main() {
    int n;
    while (scanf("%d", &n)==1) {
        int len=0, x;
        for (int i=0; i= tail[len]) tail[++len]=x;
            else tail[find_pos(x, len)] = x;
        }
        printf("%d\n", n - len);
    }
    return 0;
}

關鍵細節

  • 不下降 vs 遞增:允許相等,故二分條件為 tail[mid] > x(嚴格大於)
  • 尾值陣列性質:tail 單調不下降,保證二分正確
  • 翻轉求 LNIS:reverse 後求 LNDS 即為原序列 LNIS
  • 複雜度:O(N log N),N=30000 極快

經典地位

LNDS/LIS 是動態規劃優化的里程碑,從 O(N²) 降至 O(N log N),二分 + 貪心維護尾值是核心技巧,廣泛應用於序列分割、最小修改、Russian Doll Envelopes 等問題。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved