伴学笔记 | 豆包MarsCode AI刷题

73 阅读3分钟

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提问

解题思路

  1. 理解 unique 操作unique 操作会将相邻且相同的元素合并成一个。例如,[1, 1, 1] 经过 unique 操作后变成 [1],长度为 1。
  2. 定义 f(A)f(A) 表示序列 A 经过 unique 操作后的长度。
  3. 划分序列:我们需要将序列 S 划分为 k 段,并且使每一段都不为空。
  4. 最大化 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;
}

image.png

胜利通关,给自己和AI点个大大的赞^ ^