CF1859E E. Maximum Monogonosity题解

E. Maximum Monogonosity

题目翻译

​ 题目给我们两个长度为n的数组a和数组b,我们需要从1到n中挑选一些区间[L1, R1], [L2, R2]…满足这些区间没有相交,同时直接区间的贡献最大,定义每个区间[l, r]的贡献是std::abs(a[l] - b[r]) + std::abs(b[l] - a[r])。

思路

​ 首先我们容易思考到一个O(n * k * k)的暴力dp,我们可以定义dp[i] [j]表示考虑了前i个数,线段总长度为j的最大贡献,转移时O(k)的向前枚举即可。但这样的时间复杂度无法接受,考虑是否能够优化这个dp,绝对值不是很好处理,我们可以先把绝对值拆开,实际上只有a[l] - b[r] + b[l] - a[r], b[r] - a[l] + b[l] - a[r], a[l] - b[r] + a[r] - b[l], b[r] - a[l] + a[r] - b[l],这四种方式的转移,因为绝对值的性质,我们取其中最大的转移方式即可。

​ 注意到此时我们暴力dp的状态转移方程从dp[i] [j] = dp[i - k] [j - k] + std::abs(a[i - k + 1] - b[i]) + std::abs(b[i - k + 1] - a[i])变成了没有绝对值的形式,我们把dp[i - k] [j - k] 与 a[i - k + 1] 和 b[i - k + 1]联立在一起,发现这个转移实际上是一个前缀最大值,我们可以使用前缀和进行优化dp。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
void solve(){
int n, k;
std::cin >> n >> k;
std::vector<ll> a(n + 1, 0);
std::vector<ll> b(n + 1, 0);
for(int i = 1; i <= n; i++)
std::cin >> a[i];
for(int i = 1; i <= n; i++)
std::cin >> b[i];
std::vector dp(n + 1, std::vector<ll>(k + 1, 0));
std::vector f(n + 1, std::vector<ll>(4, -1e18));
//a[l] - b[r] + b[l] - a[r]
//-a[l] + b[r] - b[l] + a[r]
//-a[l] + b[r] + b[l] - a[r]
//a[l] - b[r] - b[l] + a[r]

for(int i = 1; i <= n; i++){
for(int j = 1; j <= std::min(k, i); j++){
f[i - j][0] = std::max(f[i - j][0], dp[i - 1][j - 1] + a[i] + b[i]);
f[i - j][1] = std::max(f[i - j][1], dp[i - 1][j - 1] - a[i] - b[i]);
f[i - j][2] = std::max(f[i - j][2], dp[i - 1][j - 1] - a[i] + b[i]);
f[i - j][3] = std::max(f[i - j][3], dp[i - 1][j - 1] + a[i] - b[i]);

dp[i][j] = dp[i - 1][j];
dp[i][j] = std::max(dp[i][j], f[i - j][0] - a[i] - b[i]);
dp[i][j] = std::max(dp[i][j], f[i - j][1] + a[i] + b[i]);
dp[i][j] = std::max(dp[i][j], f[i - j][2] - a[i] + b[i]);
dp[i][j] = std::max(dp[i][j], f[i - j][3] + a[i] - b[i]);
}
}
std::cout << dp[n][k] << endl;
}