給定長度 N ≤ 30000 的整數序列。
POJ 3670:可將序列修改為非遞減或非遞增,求最少修改元素個數。
POJ 3671:只能修改為非遞減,求最少修改個數。
最少修改次數 = N - 最長不下降子序列長度 (LNDS)。
保留 LNDS 的元素不動,其餘修改為適當值即可構成非遞減序列。
POJ 3670 額外計算最長不上升子序列 (LNIS),取兩者最大值。
tail[len] = 長度為 len 的不下降子序列的最小結尾值。
遍歷序列 x:
x ≥ tail[len]:tail[++len] = xtail 中 第一個 > x 的位置 pos,tail[pos] = x最終 len 即為 LNDS 長度。
#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
#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;
}
tail[mid] > x(嚴格大於)tail 單調不下降,保證二分正確reverse 後求 LNDS 即為原序列 LNISN=30000 極快LNDS/LIS 是動態規劃優化的里程碑,從 O(N²) 降至 O(N log N),二分 + 貪心維護尾值是核心技巧,廣泛應用於序列分割、最小修改、Russian Doll Envelopes 等問題。