题目简化后就是 LIS 将原本的价值改成了 \(a_i\),我们设 \(f_i\) 表示对于前 \(i\) 个数,必定选 \(i\),那么 \(f_i=\max\limits_{a_i>a_k}\{f_k+a_i\}\)
// #define FILE_INPUT
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;#define rep(i, a, b) for (int i = a, END##i = b; i <= END##i; i++)
#define per(i, a, b) for (int i = a, END##i = b; i >= END##i; i--)void Init();
void Solve();signed main() {cin.sync_with_stdio(0);cin.tie(0), cout.tie(0);#ifdef FILE_INPUTfreopen("input.in", "r", stdin);#endifint T = 1;// cin >> T;while (T--) {Init();Solve();}return 0;
}using LL = long long;
using ULL = unsigned long long;const int Mod = 1e9 + 7;
const int Inf = 0x3f3f3f3f;
const LL InfLL = 0x3f3f3f3f3f3f3f3f;const int N = 1010;
int n, f[N], a[N];void Init() {
}void Solve() {while (cin >> n, n) {rep(i, 1, n) cin >> a[i];int ans = 0;rep(i, 1, n) {f[i] = a[i];rep(j, 1, i - 1) if (a[i] > a[j]) {f[i] = max(f[i], f[j] + a[i]);}ans = max(ans, f[i]);}cout << ans << "\n";}
}
上面的代码时间复杂度是 \(O(n^2)\) 的,对于 \(n=10^5\) 时明显过不去,我们可以利用线段树来进行优化,从而将时间复杂度变为 \(O(n\log_2n)\),当然,线段树也可以优化 LIS 问题(虽然没必要,用二分就行)。
将这一行优化成 \(\log_2n\)
rep(j, 1, i - 1) if (a[i] > a[j]) {f[i] = max(f[i], f[j] + a[i]);
}
代码:
// #define FILE_INPUT
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;#define rep(i, a, b) for (int i = a, END##i = b; i <= END##i; i++)
#define per(i, a, b) for (int i = a, END##i = b; i >= END##i; i--)void Init();
void Solve();signed main() {cin.sync_with_stdio(0);cin.tie(0), cout.tie(0);#ifdef FILE_INPUTfreopen("input.in", "r", stdin);#endifint T = 1;// cin >> T;while (T--) {Init();Solve();}return 0;
}using LL = long long;
using ULL = unsigned long long;const int Mod = 1e9 + 7;
const int Inf = 0x3f3f3f3f;
const LL InfLL = 0x3f3f3f3f3f3f3f3f;const int N = 1010;
int n, f[N], a[N], rk[N], tot;#define lc (p << 1)
#define rc (p << 1 | 1)
int tree[N << 2], tag[N << 2];
void pushdown(int p) {if (tag[p] != -1) {tree[lc] = max(tree[lc], tag[p]);tag[lc] = max(tag[lc], tag[p]);tree[rc] = max(tree[rc], tag[p]);tag[rc] = max(tag[rc], tag[p]);tag[p] = -1;}
}
void pushup(int p) {tree[p] = max(tree[lc], tree[rc]);
}
void update(int p, int L, int R, int l, int r, int d) {if (l <= L && R <= r) {tree[p] = max(tree[p], d);tag[p] = max(tag[p], d);return;}pushdown(p);int Mid = L + R >> 1;if (l <= Mid) update(lc, L, Mid, l, r, d);if (r > Mid) update(rc, Mid + 1, R, l, r, d);pushup(p);
}
int query(int p, int L, int R, int x) {if (L == x && R == x) return tree[p];pushdown(p);int Mid = L + R >> 1;if (x <= Mid) return query(lc, L, Mid, x);return query(rc, Mid + 1, R, x);
}void Init() {
}void Solve() {while (cin >> n, n) {tot = 0;memset(tree, 0, sizeof(tree));memset(tag, -1, sizeof(tag));rep(i, 1, n) {cin >> a[i];rk[++tot] = a[i];}sort(rk + 1, rk + tot + 1);tot = unique(rk + 1, rk + tot + 1) - rk - 1;int ans = 0;rep(i, 1, n) {int x = lower_bound(rk + 1, rk + tot + 1, a[i]) - rk;f[i] = query(1, 1, tot, x) + a[i];if (x + 1 <= tot) update(1, 1, tot, x + 1, tot, f[i]);ans = max(ans, f[i]);}cout << ans << "\n";}
}
我是不会告诉你我是为了凸显时间快才用离散化的 😃