給定長度 n ≤ 10000 的序列,求最長的「Wavio 序列」長度。Wavio 序列:先嚴格遞增後嚴格遞減,且峰值唯一(即山脈形狀)。
對每個位置 i 作為峰值:
LIS[i]:以 i 結尾的最長上升子序列長度(從左向右)。LDS[i]:以 i 開頭的最長下降子序列長度(從右向左,等價於反序列的 LIS)。2 * min(LIS[i], LDS[i]) - 1。答案為所有 i 的最大值。
維護數組 d[len] 表示長度為 len 的上升子序列的最小末尾值。遍歷序列,對每個 x:
d[++maxLen] = x。d[pos] >= x,更新 d[pos] = x。#includeusing namespace std; const int MAXN = 10010; int a[MAXN], lis[MAXN], lds[MAXN], d[MAXN]; int n; int lowerBound(int *arr, int len, int val) { int l = 1, r = len; while (l <= r) { int mid = (l + r) >> 1; if (arr[mid] >= val) r = mid - 1; else l = mid + 1; } return l; } int main() { while (cin >> n) { for (int i = 1; i <= n; ++i) cin >> a[i]; // 正向 LIS int len = 0; for (int i = 1; i <= n; ++i) { int pos = lowerBound(d, len, a[i]); d[pos] = a[i]; lis[i] = pos; if (pos > len) len = pos; } // 反向 LIS(即原序列 LDS) len = 0; for (int i = n; i >= 1; --i) { int pos = lowerBound(d, len, a[i]); d[pos] = a[i]; lds[i] = pos; if (pos > len) len = pos; } int ans = 0; for (int i = 1; i <= n; ++i) ans = max(ans, 2 * min(lis[i], lds[i]) - 1); cout << ans << '\n'; } return 0; }
min。lower_bound 可用 STL lower_bound(d+1, d+len+1, x) 替換。本題是「雙向 LIS 組合」的經典應用。掌握 耐心排序法求 LIS 長度並記錄每位置值,可解決 UVA 10534、Codeforces 1010D 等變體題。