题目大意:
给定一个 (n)((1 <= n <= 10^6)) 以及一个长度为 (n) 的 正整数 数列 (a) ,其中 (1 <= a_i <= 10^9)。
假设现在你在 (i) 点,你只有两个选择 :
- 在当前点花费 (a_i) 点费用修建一个 "保卫塔"。
- 花费 (dis) 点费用修建一个 "木偶" , (dis) 表示的就是 (i) 点右侧的第一个修建了 "保卫塔"的点到 (i) 的距离。
特别的是,对于第 (n) 个点必须要选择修建 "保卫塔"。现在问你最小的费用是多少。
(example:)
(input:)
10
2 3 1 5 4 5 6 3 1 2
(output:)
18
解题思路:
状态设置:
设 (dp[i]) 表示处理完 (1 ~ i) 的所有点的最小花费并且 (i) 必须放置 "守卫塔"。
最后的答案自然是 (dp[n])
状态转移:
$dp[i] $ = (min(dp[j] + (j - i + 1) * (j - i) / 2 + A[i]) (1 <= j < i))
随便搞一搞弄个斜率优化。
处理 (i) 点的时候,我们假设 (j) < (k) ,并且选择 (j) 进行转移更优:
∵ (dp[j][1] + (j - i + 1) * (j - i) / 2 + A[i] < dp[k][1] + (k - i + 1) * (k - i) / 2 + A[i])
∴ (dp[j][1] - dp[k][1] < (k - i)^2 + (j - i)^2 + k - j)
∴ (frac{2 * (dp[j][1] - dp[k][1]) - k * (k + 1) + j * (j + 1)}{2 * (j - k)} < i)
则这时候选择 (j) 进行转移更优秀,斜率优化即可。
Code
#include <bits/stdc++.h>
using namespace std;
#define int long long
inline int read() {
int x = 0 , flag = 1;
char ch = getchar();
for( ; ch > '9' || ch < '0' ; ch = getchar()) if(ch == '-') flag = -1;
for( ; ch >= '0' && ch <= '9' ; ch = getchar()) x = (x << 3) + (x << 1) + ch - '0';
return x * flag;
}
typedef double ld;
const int MAXN = 1e6 + 50;
int n,A[MAXN],q[MAXN];
int dp[MAXN];
double calc(int j,int k) {
return (ld)(dp[j] - dp[k] + (j * (j + 1) - k * (k + 1)) / 2) / (ld)(j - k);
}
signed main() {
n = read();
for(int i = 1 ; i <= n ; i ++) A[i] = read();
int l = 1 , r = 1; q[1] = 0;
for(int i = 1 ; i <= n ; i ++) {
while(l < r && calc(q[l], q[l + 1]) < (ld)i) l ++;
dp[i] = dp[q[l]] + (i - q[l] - 1) * (i - q[l]) / 2 + A[i];
while(l <= r && calc(q[r - 1], q[r]) >= calc(q[r], i)) r --;
q[++r] = i;
}
cout << dp[n];
return 0;
}