首页 > 其他分享 >子数组最大累加和(下)

子数组最大累加和(下)

时间:2024-10-14 13:32:39浏览次数:7  
标签:最大 nums int res 累加 vector 数组

子数组最大累加和(下)

152. 乘积最大子数组

#include <vector>
#include <algorithm>

using namespace std;

class Solution {
public:
    int maxProduct(vector<int> &nums) {
        int res = nums[0];
        // 以 i 位置结尾的子数组乘积的最大值和最小值
        int minDP = nums[0];
        int maxDP = nums[0];
        // 最大值可能是当前元素
        // 或者是以 i-1 位置结尾的最小乘积与 nums[i] 相乘(负数乘负数)
        // 或者以 i-1 位置结尾的最大乘积与 nums[i] 相乘
        // 在递推过程中,记录以 i-1 结尾的子数组乘积的最值
        for (int i = 1, curMin, curMax; i < nums.size(); ++i) {
            curMin = min(nums[i], min(nums[i] * minDP, nums[i] * maxDP));
            curMax = max(nums[i], max(nums[i] * minDP, nums[i] * maxDP));
            minDP = curMin;
            maxDP = curMax;
            res = max(res, maxDP);
        }
        return res;
    }
};

子序列累加和必须被7整除的最大累加和

给定一个非负数组 nums,可以任意选择数字组成子序列,但是子序列的累加和必须被7整除,返回最大累加和。

#include <vector>
#include <algorithm>
#include <iostream>
#include <cstdlib>

using namespace std;

class Solution {

public:
    // 暴力递归,作为验证方法
    int maxSum1(vector<int> &nums) {
        return recursive(nums, 0, 0);
    }

    int recursive(vector<int> &nums, int i, int s) {
        if (i == nums.size())
            return s % 7 == 0 ? s : 0;
        return max(recursive(nums, i + 1, s), recursive(nums, i + 1, s + nums[i]));
    }

    // 正式方法,时间复杂度 O(n)
    int maxSum2(vector<int> &nums) {
        int n = nums.size();
        // dp[i][j] 表示 nums 前 i 个数形成的子序列,子序列累加和模 7 等于 j
        // 这样的子序列最大累加和是多少
        // dp[i][j] == -1 表示不存在
        vector<vector<int>> dp(n + 1, vector<int>(7, -1));
        // i = 0 的情况,只有模 7 为 0 的情况存在,且累加和为 0
        dp[0][0] = 0;
        fill(dp[0].begin() + 1, dp[0].end(), -1);
        // i > 0 的情况
        for (int i = 1; i <= n; i++) {
            // 当前数字
            int cur = nums[i - 1];
            // 当前数字模 7 的余数
            int reminder = nums[i - 1] % 7;
            for (int j = 0; j < 7; j++) {
                // 子序列不要当前数字的情况,最大累加和就是之前的
                dp[i][j] = dp[i - 1][j];
                // 还需要的余数
                int need = (7 + j - reminder) % 7;
                // 子序列要当前数字的情况
                if (dp[i - 1][need] != -1)
                    dp[i][j] = max(dp[i][j], dp[i - 1][need] + cur);
            }
        }
        return dp[n][0];
    }
};

vector<int> randomArray(int n, int v) {
    vector<int> arr(n);
    for (int i = 0; i < n; i++)
        arr[i] = rand() % v;
    return arr;
}

int main() {
    Solution s;
    int n = 15;
    int v = 30;
    int testTime = 20000;
    cout << "测试开始" << endl;
    for (int i = 0; i < testTime; i++) {
        int len = rand() % n + 1;
        vector<int> nums = randomArray(len, v);
        int ans1 = s.maxSum1(nums);
        int ans2 = s.maxSum2(nums);
        if (ans1 != ans2) cout << "出错了!" << endl;
    }
    cout << "测试结束" << endl;
}

魔法卷轴

给定一个数组 nums,其中可能有正、负、0。每个魔法卷轴可以把nums中连续的一段全变成0,你希望数组整体的累加和尽可能大。卷轴使不使用、使用多少随意,但一共只有2个魔法卷轴,返回数组尽可能大的累加和。

#include <vector>
#include <algorithm>
#include <iostream>
#include <cstdlib>

using namespace std;

class Solution {
public:
    // 暴力递归
    int maxSum1(const vector<int> &nums) {
        int n = nums.size();
        // 不用卷轴
        int p1 = 0;
        for (int num: nums) p1 += num;
        // 用一个卷轴
        int p2 = mustOneScroll(nums, 0, n - 1);
        // 用两个卷轴
        int p3 = INT_MIN;
        for (int i = 0; i <= n - 2; i++)
            p3 = max(p3, mustOneScroll(nums, 0, i) + mustOneScroll(nums, i + 1, n - 1));
        return max(p1, max(p2, p3));
    }

    // nums[start...end] 范围上用一次卷轴的最大累加和
    int mustOneScroll(const vector<int> &nums, int start, int end) {
        int res = INT_MIN;
        // [l, r] 变成 0
        for (int l = start; l <= end; l++) {
            for (int r = l; r <= end; r++) {
                int curAns = 0;
                // 累加 [l, r] 范围外的
                for (int i = start; i < l; i++)
                    curAns += nums[i];
                for (int i = r + 1; i <= end; i++)
                    curAns += nums[i];
                res = max(res, curAns);
            }
        }
        return res;
    }

    // 正式方法,时间复杂度 O(n)
    int maxSum2(const vector<int> &nums) {
        int n = nums.size();
        if (n == 0) return 0;

        // 不用卷轴
        int p1 = 0;
        for (int num: nums) p1 += num;

        // prefix[i]: 0 ~ i 范围上用一次卷轴的情况下,0 ~ i 范围上整体最大累加和多少
        vector<int> prefix(n);
        // 前缀和
        int prefixSum = nums[0];
        // 前缀和的最大值
        int maxPrefixSum = max(0, nums[0]);
        for (int i = 1; i < n; i++) {
            // 在 i 位置之前已经用过卷轴:最大累加和就要加上当前数字,prefix[i - 1] + nums[i]
            // 在 i 位置之前没有用过卷轴:最大累加和就是之前前缀和的最大值,使用卷轴的部分是最大前缀和出现的地方到当前位置
            prefix[i] = max(prefix[i - 1] + nums[i], maxPrefixSum);
            // 更新
            prefixSum += nums[i];
            maxPrefixSum = max(maxPrefixSum, prefixSum);
        }
        // 用一个卷轴
        int p2 = prefix[n - 1];

        // suffix[i] : i ~ n - 1 范围上用一次卷轴的情况下,i ~ n - 1 范围上整体最大累加和多少
        vector<int> suffix(n);
        int suffixSum = nums[n - 1];
        int maxSuffixSum = max(0, suffixSum);
        for (int i = n - 2; i >= 0; i--) {
            suffix[i] = max(nums[i] + suffix[i + 1], maxSuffixSum);
            suffixSum += nums[i];
            maxSuffixSum = max(maxSuffixSum, suffixSum);
        }
        // 用两个卷轴
        int p3 = INT_MIN;
        for (int i = 1; i < n; i++)
            p3 = max(p3, prefix[i - 1] + suffix[i]);

        return max(p1, max(p2, p3));
    }
};

vector<int> randomArray(int n, int v) {
    vector<int> ans(n);
    for (int i = 0; i < n; i++)
        ans[i] = rand() % (v * 2 + 1) - v;
    return ans;
}

int main() {
    Solution s;
    int n = 50;
    int v = 100;
    int testTime = 10000;
    cout << "测试开始" << endl;
    for (int i = 0; i < testTime; i++) {
        int len = rand() % n;
        vector<int> nums = randomArray(len, v);
        int ans1 = s.maxSum1(nums);
        int ans2 = s.maxSum2(nums);
        if (ans1 != ans2) {
            cout << "出错了!" << endl;
        }
    }
    cout << "测试结束" << endl;
}

689. 三个无重叠子数组的最大和

#include <vector>

using namespace std;

class Solution {
public:
    vector<int> maxSumOfThreeSubarrays(vector<int> &nums, int k) {
        int n = nums.size();
        // sums[i]: 以 i 开头并且长度为 k 的子数组的累加和
        vector<int> sums(n);
        for (int l = 0, r = 0, sum = 0; r < n; r++) {
            sum += nums[r];
            if (r - l + 1 == k) {
                sums[l] = sum;
                sum -= nums[l];
                l++;
            }
        }

        // prefix[i]: 0 ~ i 范围上所有长度为 k 的子数组中,拥有最大累加和的子数组,是以什么位置开头的
        vector<int> prefix(n);
        // prefix[0, k-2] 位置的子数组长度都没到 k,不需要填
        // prefix[k-1] 子数组长度为 k,以 0 开头
        for (int l = 1, r = k; r < n; l++, r++) {
            // sums[l] 是 r++ 后新增加的,需要考虑的长度为 k 的子数组累加和
            if (sums[l] > sums[prefix[r - 1]]) {
                // 新增的更大
                prefix[r] = l;
            } else {
                // 原来在 0 ~ r - 1 上最大累加和更大
                prefix[r] = prefix[r - 1];
            }
        }

        // suffix[i]: i ~ n - 1 范围上所有长度为 k 的子数组中,拥有最大累加和的子数组,是以什么位置开头的
        vector<int> suffix(n);
        suffix[n - k] = n - k;
        for (int l = n - k - 1; l >= 0; l--) {
            if (sums[l] >= sums[suffix[l + 1]]) {
                suffix[l] = l;
            } else {
                suffix[l] = suffix[l + 1];
            }
        }

        int a = 0, b = 0, c = 0, maxSum = 0;
        // 中间的子数组 [i, j] 长度 k
        for (int p, s, i = k, j = 2 * k - 1, sum; j < n - k; i++, j++) {
            // [0, i - 1] 上的最大累加和的子数组以 p 开头
            p = prefix[i - 1];
            // [i + 1, n - 1] 上的最大累加和的子数组以 s 开头
            s = suffix[j + 1];
            sum = sums[p] + sums[i] + sums[s];
            if (sum > maxSum) {
                maxSum = sum;
                a = p;
                b = i;
                c = s;
            }
        }
        return {a, b, c};
    }
};

可以翻转1次的情况下子数组最大累加和

给定一个数组 nums,现在允许你随意选择数组连续一段进行翻转,也就是子数组逆序的调整。返回必须随意翻转 1 次之后,子数组的最大累加和。

#include <vector>
#include <iostream>
#include <algorithm>
#include <cstdlib>

using namespace std;

class Solution {
public:
    // 暴力方法
    int maxSumReverse1(vector<int> &nums) {
        int res = INT_MIN;
        for (int l = 0; l < nums.size(); l++) {
            for (int r = l; r < nums.size(); r++) {
                reverse(nums, l, r);
                res = max(res, maxSum(nums));
                reverse(nums, l, r);
            }
        }
        return res;
    }

    // nums[l...r] 范围上的数字进行逆序调整
    void reverse(vector<int> &nums, int l, int r) {
        while (l < r)
            swap(nums[l++], nums[r--]);
    }

    // 返回子数组最大累加和
    int maxSum(const vector<int> &nums) {
        int n = nums.size();
        int res = nums[0];
        for (int i = 1, pre = nums[0]; i < n; i++) {
            pre = max(nums[i], pre + nums[i]);
            res = max(res, pre);
        }
        return res;
    }

    // 正式方法,时间复杂度 O(n)
    int maxSumReverse2(vector<int> &nums) {
        int n = nums.size();
        // start[i]: 以 i 开头的子数组中,最大累加和是多少
        vector<int> start(n);
        start[n - 1] = nums[n - 1];
        for (int i = n - 2; i >= 0; i--)
            start[i] = max(nums[i], nums[i] + start[i + 1]);

        int res = start[0];
        // end: 子数组必须以 i - 1 结尾,其中的最大累加和
        int end = nums[0];
        // 0 ~ i - 1 上以某个位置结尾的子数组的最大累加和
        int maxEnd = nums[0];
        for (int i = 1; i < n; i++) {
            res = max(res, maxEnd + start[i]);
            end = max(nums[i], end + nums[i]);
            maxEnd = max(maxEnd, end);
        }
        // 不用翻转的情况
        res = max(res, maxEnd);
        return res;
    }
};

vector<int> randomArray(int n, int v) {
    vector<int> ans(n);
    for (int i = 0; i < n; i++)
        ans[i] = rand() % (v * 2 + 1) - v;
    return ans;
}

int main() {
    Solution s;
    int n = 50;
    int v = 200;
    int testTime = 20000;
    cout << "测试开始" << endl;
    for (int i = 0; i < testTime; i++) {
        int len = rand() % n + 1;
        vector<int> arr = randomArray(len, v);
        int ans1 = s.maxSumReverse1(arr);
        int ans2 = s.maxSumReverse2(arr);
        if (ans1 != ans2)
            cout << "出错了!" << endl;
    }
    cout << "测试结束" << endl;
}

删掉1个数字后长度为k的子数组最大累加和

给定一个数组 nums,求必须删除一个数字后的新数组中,长度为 k 的子数组最大累加和,删除哪个数字随意

#include <vector>
#include <iostream>
#include <algorithm>
#include <cstdlib>

using namespace std;

class Solution {
public:
    // 暴力方法
    int maxSum1(vector<int> &nums, int k) {
        int n = nums.size();
        if (n <= k) return 0;
        int res = INT_MIN;
        for (int i = 0; i < n; i++) {
            vector<int> rest = deleteElement(nums, i);
            res = max(res, lenKMaxSum(rest, k));
        }
        return res;
    }

    // 删掉 index 位置的元素,然后返回新数组
    vector<int> deleteElement(vector<int> &nums, int index) {
        vector<int> res;
        for (int j = 0; j < nums.size(); j++)
            if (j != index)
                res.push_back(nums[j]);
        return res;
    }

    // 枚举每一个子数组找到最大累加和
    int lenKMaxSum(vector<int> &nums, int k) {
        int n = nums.size();
        int res = INT_MIN;
        for (int i = 0; i <= n - k; i++) {
            int cur = 0;
            for (int j = i, cnt = 0; cnt < k; j++, cnt++)
                cur += nums[j];
            res = max(res, cur);
        }
        return res;
    }

    // 正式方法:时间复杂度 O(N)
    int maxSum2(vector<int> &nums, int k) {
        int n = nums.size();
        if (n <= k) return 0;
        // 单调队列: 维持窗口内最小值的更新结构
        vector<int> queue(n);
        int l = 0, r = 0;
        // 窗口累加和
        long sum = 0;
        int res = INT_MIN;
        for (int i = 0; i < n; i++) {
            // i 位置进入单调队列
            while (l < r && nums[queue[r - 1]] >= nums[i])
                r--;
            queue[r++] = i;
            sum += nums[i];
            if (i >= k) {
                res = max(res, (int) (sum - nums[queue[l]]));
                // 如果单调队列最左侧的位置过期了,从队列中弹出
                if (queue[l] == i - k) l++;
                sum -= nums[i - k];
            }
        }
        return res;
    }
};

// 生成长度为 n,值在 [-v, +v] 之间的随机数组
vector<int> randomArray(int n, int v) {
    vector<int> ans(n);
    for (int i = 0; i < n; i++)
        ans[i] = rand() % (2 * v + 1) - v;
    return ans;
}

int main() {
    Solution s;
    int n = 200;
    int v = 1000;
    int testTimes = 10000;
    cout << "测试开始" << endl;
    for (int i = 0; i < testTimes; i++) {
        int len = rand() % n + 1;
        vector<int> nums = randomArray(len, v);
        int k = rand() % n + 1;
        int ans1 = s.maxSum1(nums, k);
        int ans2 = s.maxSum2(nums, k);
        if (ans1 != ans2)
            cout << "出错了!" << endl;
    }
    cout << "测试结束" << endl;
}

标签:最大,nums,int,res,累加,vector,数组
From: https://www.cnblogs.com/sprinining/p/18463937

相关文章

  • Java数组工具类Arrays
    Arrays工具类将数组内容转为字符串对数组内容进行排序判断是否相同数组的复制查找特定值的索引用指定元素存满数组数组转列表Arrays工具类提供了一系列方便、高效的方法来操作和处理数组,大大简化了Java中对数组的常见操作。将数组内容转为字符串使用Arrays......
  • Bash数组与字典使用
    需求编写bash脚本希望用到更加灵活的数据格式。数组使用index索引,直接调用返回第一个元素,支持-1检索,不存在的index返回空。举例:将十进制数转换为十六进制。declare-aHEX#可以省略HEX=(0123456789ABCDEF)echo${HEX[15]}#F字典使用key索引,直接调用......
  • 【算法】树状数组
    1.算法简介先来看一个很现实的问题:就拿[luogu]P3372【模板】线段树1这道题为例。按常规做法,应该是用普通线段树+\(lazytag\)即可,但这样做代码较长,达到了\(118\)行。而如果用树状数组去做,只用\(63\)行就能搞定,用时更短,代码也很好理解。以下是数据对比:很明显,在两......
  • 2024-10-13:用go语言,给定一个二进制数组 nums,长度为 n, 目标是让 Alice 通过最少的行动
    2024-10-13:用go语言,给定一个二进制数组nums,长度为n,目标是让Alice通过最少的行动次数从nums中拾取k个1。Alice可以选择任何索引aliceIndex,如果对应的nums[aliceIndex]是1,Alice会拾取一个1并将其设为0。之后,Alice可以选择以下两种行动之一:将一个0变为1(最多执行maxCh......
  • 修改Linux系统打开最大句柄数?
    在Linux系统中,修改系统打开的最大句柄数(通常称为文件描述符数)是一个系统管理员可能会遇到的常见任务。以下是一个详细的步骤说明,包括如何查看当前限制和如何修改这些限制。一、查看当前限制在修改之前,了解当前的句柄数限制是很重要的。这可以通过几种方式来完成:查看用户级限制......
  • Java——数组的定义与使用
    各位看官:如果您觉得这篇文章对您有帮助的话欢迎您分享给更多人哦感谢大家的点赞收藏评论,感谢您的支持!!!一:数组的概念以及定义,初始化1.1:数组概念以及定义数组概念:可以看成是相同类型元素的一个集合。数组定义:三种方法T[]数组名=newT[N];例如:int[]a......
  • [SDOI2017] 新生舞会——二分 最大费用最大流
    [SDOI2017]新生舞会题目描述学校组织了一次新生舞会,Cathy作为经验丰富的老学姐,负责为同学们安排舞伴。有\(n\)个男生和\(n\)个女生参加舞会,一个男生和一个女生一起跳舞,互为舞伴。Cathy收集了这些同学之间的关系,比如两个人之前认识没,计算得出\(a_{i,j}\)。Cathy还需......
  • Go 语言中的数组使用
    Go语言中的数组使用本文将详细介绍Go语言中数组的各种用法,包括数组声明和定义、多维数组的使用和遍历,以及数组的值传递等初级、中级和高级用法。数组声明定义的几种方式初级用法介绍Go中的数组是一种固定长度、元素类型相同的数据结构,适用于简单的数据存储场景。......
  • DAY31 ||贪心算法基础 | 455.分发饼干 |376.摆动序列 |53.最大子数组和
    贪心算法基础贪心算法是一种在求解问题时采取逐步构建解决方案的算法策略。它通过在每一步选择在当前看来最优的选择(即“贪心”选择),希望通过局部最优解的累积得到全局最优解。贪心算法的核心思想局部最优:每一步都选择在当前状态下最优的选择,不考虑后续步骤可能带来的影响。......
  • 02 线性结构——数组(特性、优缺点、基本使用、可变长的动态数组)
    目录1数组基础知识1.1认识数组1.2数组的声明1.3 数组的特性2数组的优缺点2.1优点2.1.1查找容易2.1.2高效的访问和修改2.2缺点2.2.1插入和删除效率低2.2.2扩展相对繁琐3数组的基本使用3.1遍历数组3.2修改数组元素4可变长的动态数组4.1 实现原理......