《最长递增子数组问题》和《小C不怕困难》| 豆包MarsCode AI刷题

139 阅读5分钟

今天我们将在豆包MarsCode AI刷题平台上,完成《最长递增子数组问题》与《小C不怕困难》这两个算法问题,通过这些练习提升用户解决此类问题的能力

《最长递增子数组问题》题面如下:

图片.png

问题理解

题目要求我们在一个长度为 n 的数组 ab 中,通过选择元素生成一个新的数组 c,使得 c 中的最长递增子数组满足相邻两个元素的差的绝对值为 1。我们需要找到这个最长递增子数组的长度。

数据结构选择

我们定义一个三维动态规划数组 f,其中:

  • f[i][j][0] 表示在 a 数组中选择了前 i 个元素,从 b 数组中选择了前 j 个元素,且最后一个元素来自 a 数组时,最长的递增子数组长度。
  • f[i][j][1] 表示在 a 数组中选择了前 i 个元素,从 b 数组中选择了前 j 个元素,且最后一个元素来自 b 数组时,最长的递增子数组长度。

算法步骤

  1. 初始化

    • 对于 f[i][0][0]f[0][j][1],分别表示只从 ab 中选择元素的情况。如果当前元素与前一个元素的差为 1,则更新长度。
  2. 状态转移

    • 对于每个 ij,考虑从 ab 中选择当前元素,并更新 f[i+1][j+1][0]f[i+1][j+1][1]

      • 如果 a[i]b[j] 的差为 1,则可以由 b 转移到 a,或者由 a 转移到 b
      • 如果 a[i] 与前一个 a[i-1] 的差为 1,则可以从由a 转移到 a
      • 如果 b[j] 与前一个 b[j-1] 的差为 1,则可以由 b 转移到 b
  3. 结果

    • 最终结果为 f[i+1][j+1][0]f[i+1][j+1][1] 中的最大值。

具体实现

int solution(vector<int> a, vector<int> b) {
    int n = a.size();
    vector<vector<vector<int>>> f(n+1,vector<vector<int>>(n+1,vector<int>(2,1)));
    
    // 初始化
    for(int i=0;i<n;i++){
        if(i>0 && a[i] == a[i-1]+1){
            f[i+1][0][0] = max(1, f[i][0][0]+1);
        }
    }
    for(int j=0;j<n;j++){
        if(j>0 && b[j] == b[j-1]+1){
            f[0][j+1][1] = max(1, f[0][j][1]+1);
        }
    }
    
    int ret=1;
    for(int i=0;i<n;i++){
        for(int j=0;j<n;j++){
            // 由 b 转移到 a
            if(a[i] == b[j]+1){
                f[i+1][j+1][0] = max(f[i+1][j+1][0], f[i][j+1][1]+1);
            }
            // 由 a 转移到 a
            if(i>0 && a[i] == a[i-1]+1){
                f[i+1][j+1][0] = max(f[i+1][j+1][0], f[i][j+1][0]+1);
            }
            // 由 a 转移到 b
            if(b[j] == a[i]+1){
                f[i+1][j+1][1] = max(f[i+1][j+1][1], f[i+1][j][0]+1);
            }
            // 由 b 转移到 b
            if(j>0 && b[j] == b[j-1]+1){
                f[i+1][j+1][1] = max(f[i+1][j+1][1], f[i+1][j][1]+1);
            }
            // 更新结果
            ret = max({ret, f[i+1][j+1][0], f[i+1][j+1][1]});
        }
    }
    return ret;
}

《小C不怕困难》题面如下:

图片.png

问题理解

小C需要依次与 n 个怪物战斗,每个怪物的战斗力为 aia_i。小C的初始战斗力为 0。战斗规则如下:

  1. 如果小C的战斗力小于怪物的战斗力,小C会触发被动技能,将自己的战斗力提升至怪物的战斗力,并战胜这个怪物,同时小C的勇气值增加 aixa_i - x
  2. 如果小C的战斗力大于或等于怪物的战斗力,他会直接战胜这个怪物,战斗结束后,小C的战斗力会降低至怪物的战斗力。

小C可以自由决定与怪物战斗的顺序,目标是最大化累计的勇气值。

思路提示

  1. 排序

    • 由于小C可以自由决定战斗顺序,我们可以考虑对怪物的战斗力进行排序。排序后,我们可以更容易地决定战斗的顺序,进而最大化勇气值
  2. 贪心策略

    • 我们这里采取的贪心策略是:每次选取两个差值较大的怪物,先与战斗力较小的怪物战斗降低战力,然后再与战斗力较大的怪物战斗提升战力。这样可以最大化每次战斗时勇气值的增加
    • 具体实现时,将根据怪物战斗力排序后的怪物分为前后两组,每次分别在前面一组和后面一组选出一个之前未选过且战力最小的怪物,两者的差值就是先后与两个怪物战斗后提升的勇气值

具体实现

#include <bits/stdc++.h>

using namespace std;

int solution(int n, vector<int>& a) {
    // write code here
    // 将初始战力也放到数组中
    a.push_back(0);
    // 对怪物的战斗力进行排序
    sort(a.begin(), a.end());
    // 放入了初始战力这个新元素,更新数组长度
    n++;
    
    int ret=0;
    // 每次分别在前面一组和后面一组选出一个之前未选过且战力最小的怪物
    for(int i=0;i<n/2;i++){
        // 两者的差值就是先后与两个怪物战斗后提升的勇气值
        ret += a[i+(n+1)/2] - a[i];
    }
    return ret;
}

int main() {
    vector<int> a1 = {1, 2};
    cout << (solution(2, a1) == 2) << endl;
    
    vector<int> a2 = {1, 2, 2};
    cout << (solution(3, a2) == 3) << endl;
    
    vector<int> a3 = {3, 6, 2, 8};
    cout << (solution(4, a3) == 12) << endl;
    
    return 0;
}

借助豆包MarsCode AI刷题平台,我们不仅高效地解决了《最长递增子数组问题》和《小C不怕困难》,还加深了对相关算法和数据结构的理解,后续会借助豆包MarsCode AI给大家展示更多题目的解法。