MarsCode AI刷题应用反馈
254.序列划分与最大化函数值
问题描述
小U拿到了一个长度为n的序列S,他想通过某种方法将这个序列划分为k段,并且使每一段都不为空。他发现,通过对每一段应用一个unique操作(即将相邻且相同的元素合并成一个)之后,可以得到一个新的序列,并定义函数f(A)f(A)表示经过unique操作后的序列的长度。小U想知道,如何划分才能使所有段的$f`函数值之和最大化?
例如,给定序列 [1,1,1,2,2,3,3,1],小U可以将其划分为三段,分别为 [1,1,1]、[2,2] 和 [3,3,1],unique 之后得到的序列分别是 [1]、[2] 和 [3,1],那么最终的ff函数值之和为 6。
刚看完题,一头雾水,带着对题目的疑问向AI求助。
小U有一个长度为 n 的序列 S,他希望通过某种方法将这个序列划分为 k 段,并且使每一段都不为空。对于每一段,小U可以应用一个 unique 操作,即将相邻且相同的元素合并成一个。例如,序列 [1, 1, 1, 2, 2, 3, 3, 1] 经过 unique 操作后可能变成 [1, 2, 3, 1]。
我们定义函数 f(A) 表示经过 unique 操作后的序列 A 的长度。小U想知道,如何划分序列 S 才能使所有段的 f 函数值之和最大化。
经过AI的初步描述,我对题目开始有了理解,但至于解题,思路还是不太清楚,继续向AI提问
解题思路
- 理解
unique操作:unique操作会将相邻且相同的元素合并成一个。例如,[1, 1, 1]经过unique操作后变成[1],长度为 1。 - 定义
f(A):f(A)表示序列A经过unique操作后的长度。 - 划分序列:我们需要将序列
S划分为k段,并且使每一段都不为空。 - 最大化
f函数值之和:目标是找到一种划分方式,使得所有段的f函数值之和最大化。
在AI的帮助下,解题思路我也有了,开心!!!
最后在借鉴下,AI的编程思路,简单对其修改,结束战斗。
#include <iostream>
#include <vector>
using namespace std;
int solution(int n, int k, vector<int> a) {
// 定义 DP 表和前缀去重长度数组 s
vector dp(n + 1, vector<int>(k + 1, 0));
vector<int> s(n + 1, 0);
// 计算前缀去重长度数组 s
s[1] = 1;
for (int i = 2; i <= n; ++i) {
if (a[i - 1] != a[i - 2]) s[i] = s[i - 1] + 1;
else s[i] = s[i - 1];
}
// 引入滚动数组 ma 以优化 DP 转移方程
vector<int> ma(k + 1, 0);
// 外层循环遍历每个右端点 r
for (int r = 1; r <= n; ++r) {
// 倒序更新分段数量,确保 DP 转移方程的正确性
for (int i = k; i > 0; --i) {
// 更新当前 dp 值,直接使用前缀和 s[r] 与最优状态 ma[i-1]
dp[r][i] = max(dp[r][i], s[r] + ma[i - 1]);
// 更新 ma[i],维护当前最优值
ma[i] = max(ma[i], dp[r][i] - s[r] + (r < n && a[r - 1] == a[r]));
}
}
// 返回结果,即划分为 k 段的最大 f 值之和
return dp[n][k];
}
int main() {
cout << (solution(8, 3, {1, 1, 1, 2, 2, 3, 3, 1}) == 6) << endl;
cout << (solution(6, 2, {1, 2, 3, 3, 2, 1}) == 6) << endl;
cout << (solution(5, 1, {1, 1, 1, 1, 1}) == 1) << endl;
return 0;
}