1. 程式人生 > 其它 >3662. 最大上升子序列和

3662. 最大上升子序列和

給定一個長度為nn的整數序列a1,a2,,ana1,a2,…,an。

請你選出一個該序列的嚴格上升子序列,要求所選子序列的各元素之和儘可能大。

請問這個最大值是多少?

輸入格式

第一行包含整數nn。

第二行包含nn個整數a1,a2,,ana1,a2,…,an。

輸出格式

輸出最大的上升子序列和。

資料範圍

對於前三個測試點,1n41≤n≤4。
對於全部測試點,1n105,1ai1091≤n≤105,1≤ai≤109。

輸入樣例1:

2
100 40

輸出樣例1:

100

輸入樣例2:

4
1 9 7 10

輸出樣例2:

20

樣例解釋

對於樣例11,我們只選取100100。

對於樣例22,我們選取1,9,10

1,9,10。

演算法 —— 離散化 + 樹狀陣列
用 sum[i] 表示以 a[i] 為結尾的最大單調子序列和
用 maxSum[x] 表示以 x 為結尾值的最大單調子序列和

暴力解法:

for (int i = 1; i <= n; i++) {
    sum[i] = a[i];
    for (int j = 1; j < a[i]; j++) {
        sum[i] = max(sum[i], maxSum[j] + a[i]);
    }
    maxSum[a[i]] = max(maxSum[a[i]], sum[i]);
}

可以使用樹狀陣列優化,將 maxSum 優化成單點修改、區間查詢的樹狀陣列。
需要注意的是,資料的範圍為 1e9,比較大,還需要進行離散化。

#include <algorithm>
#include <cstring>
#include <iostream>
#include <unordered_map>
using namespace std;
const int N = 1e5 + 10;
typedef long long LL;
LL n, a[N], b[N], sum[N], maxSum[N];
unordered_map<int, LL> mp;
LL c[N];
// 查詢字首和:查詢序列 a 第 1~x 個數的和
LL ask(LL x) {
    LL ans 
= 0; for (; x; x -= x & -x) ans = max(ans, c[x]); return ans; } // 單點增加:給序列中的一個數 a[x] 加上 y // 演算法:自下而上每個節點都要增加 y void add(int x, LL y) { for (; x <= n; x += x & -x) c[x] = max(c[x], y); } int main() { cin >> n; for (int i = 1; i <= n; i++) cin >> a[i]; // 離散化 memcpy(b, a, sizeof a); sort(b + 1, b + n + 1); LL m = 0; for (int i = 1; i <= n; i++) { if (!mp.count(b[i])) mp[b[i]] = ++m; } // DP 過程,使用樹狀陣列優化 for (int i = 1; i <= n; i++) { sum[i] = max(mp[a[i]], ask(mp[a[i]] - 1) + a[i]); add(mp[a[i]], sum[i]); // sum[i] = a[i]; // for (int j = 1; j < a[i]; j++) { // sum[i] = max(sum[i], maxSum[j] + a[i]); // } // maxSum[a[i]] = max(maxSum[a[i]], sum[i]); } LL ans = 0; for (int i = 1; i <= n; i++) { ans = max(ans, sum[i]); } cout << ans << endl; return 0; }