給定一個序列a[i],找一個a[i]的連續子序列b[i],使得滿足在所有的情況中 最大。(m爲b的長度)
設 dp[i] 表示以第 i 位爲結束位置的最優解。
設 f[i] = a[1]*1+a[2]*2+...+a[n]*n 。
設 sum[i] = a[1]+a[2]+...+a[n] 。
設 j 爲b序列的起始位置的前一位(即起始位置是 j+1),因此 0<=j<i 。
動態轉移方程:dp[i] = max( f[i] - f[j] - j*(sum[i]-sum[j]) ) (0<=j<i) 。
此時複雜度爲O(n^2),進行斜率優化:
設 j 點優於 k 點,且 j > k ,則:
左邊式子表示以 i 爲x,以 i*sum[i]-f[i] 爲y的直線的斜率,由於是>sum[i],所以要維護上凸性質。
可知:當時,
j點優於 k 點及之前的所有點 且 優於 i 點及之後的所有點,即 j 點爲最優點。因此找第一個斜率小於sum[i]的即爲最優點。
由於a[i]中存在負數,所以sum[i]不單調,不能有單調隊列找最優點,所以二分查找最優點即可。最後維護一個上凸的性質。
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int MAX = 2e5 + 100; const ll MOD = 1e9 + 7; int n; int a[MAX]; ll f[MAX], sum[MAX]; ll dp[MAX]; // dp[i]=max(f[i]-f[j]-j*(sum[i]-sum[j])) 0<=j<i ll q[MAX]; ll up(int j, int k) { return (j * sum[j] - f[j]) - (k * sum[k] - f[k]); } ll down(int j, int k) { return j - k; } int bin_search(int l, int r, ll val) { int ans = l; while (l < r) { int mid = (l + r) >> 1; if (mid<r&&up(q[mid + 1], q[mid]) > val * down(q[mid + 1], q[mid])) { ans = mid + 1; l = mid + 1; } else { r = mid; } } return ans; } int main() { scanf("%d", &n); for (int i = 1; i <= n; i++) scanf("%d", &a[i]); for (int i = 1; i <= n; i++) { f[i] = f[i - 1] + a[i] * i; sum[i] = sum[i - 1] + a[i]; } dp[0] = 0; int left = 1, right = 1; q[1] = 0; ll ans = -(1e9 + 7); for (int i = 1; i <= n; i++) { int x = bin_search(left, right, sum[i]); int front = q[x]; dp[i] = f[i] - f[front] - front * (sum[i] - sum[front]); while (left < right&&up(q[right], q[right - 1])*down(i, q[right]) <= down(q[right], q[right - 1])*up(i, q[right])) right--; q[++right] = i; ans = max(ans, dp[i]); } printf("%lld\n", ans); return 0; }