算法设计与分析[0009] Dynamic Programming(II)(Maximum Sum/Product Subarray)

原文引用https://www.dazhuanlan.com/2019/08/25/5d625b5c4d1ea/

 本文通过 53. Maximum Subarray & 152. Maximum Product Subarray 分析根据动态规划思路进行问题求解中的一个关键环节:子问题的拆分和求解。

Problem Description

  • 两道题解决的问题相似,都是求解给定序列中满足某种数学特征(和最大/乘积最大)的子序列,虽然不需要将该子序列输出。
  • 留意的关键字眼是:containing at least one number,所以给定序列至少有一个元素,这也启发我们可以将其作为特殊处理。

53. Maximum Subarray 解题思路

  • 思路一:$sums[j]$ 为序列前 j 个元素的最大子段和作为求解的子问题,则 $sum[n]$ 则为问题的答案。然而,如何利用 $sums[1, 2, …, j-1]$ 对 $sums[j]$ 进行求解呢?显然需要知道前 j 个元素的最大字段和的子段起始和终止位置,求解这个子问题的状态迁移显然比较复杂。
  • 换一种思路。思路二:$sums[j]$ 为以第 j 个元素为结尾的子段的最大子段和作为求解的子问题,$max_{1 leq j leq n}(sums[j])$ 即为整个序列的最大子段和。而通过 $sums[j-1]$ 和当前元素 $nums[j]$ 即可计算以第 j 个元素为结尾的最大子段和 $sums[j]$,状态转移方程 如下:
    $$ sums[j+1] = begin{cases} nums[j+1]      sums[j] lt 0 cr sums[j] + nums[j+1] others end{cases}$$
  • 根据思路二,53. Maximum Subarray 解答如下:
12345678910111213141516171819202122232425
class  {public:    int maxSubArray(vector<int>& nums) {        int SizeofNums = nums.size();        if(SizeofNums == 1) {            return nums[0];        }        int sums[SizeofNums];        sums[0] = nums[0];        for(int i=1; i<SizeofNums; i++) {

sums[i] = sums[i-1]<0 ? nums[i] : sums[i-1]+nums[i];         }

int largestSum = sums[0];        for(int i=1; i<SizeofNums; i++) {            if(largestSum < sums[i]) {                largestSum = sums[i];            }        }

return largestSum;    }};
  • 为了得到 largestSum 对应的子序列,我们可以通过变量 startIdx 记录以第 j 个元素结尾(endIdx)的最大子段和对应子序列的起始位置,$nums[startIdx, …, endIdx]$ 即为对应的子序列;另外,考虑到当前状态只与前一个状态有关,所以可以使用变量代替数组,节省内存,同时,避免获取The largest sum of the whole array 时的重复循环。
12345678910111213141516171819202122232425262728293031
class Solution {public:    int maxSubArray(vector<int>& nums) {        int SizeofNums = nums.size();        if(SizeofNums == 1) {            return nums[0];        }        // largest sum for the subarray ending with current element         int curSum = nums[0];        // largest sum of subarray for the whole array        int largestSum = curSum;        // subarray[startIdx, endIdx] with largest sum for the whole array        int startIdx = 0, endIdx = 0;        for(int i=1; i<SizeofNums; i++) {            if(curSum < 0) {                curSum = nums[i];                startIdx = i;            }            else {                curSum = curSum + nums[i];            }

if(curSum > largestSum) {                largestSum = curSum;                endIdx = i;            }        }

return largestSum;    }};

152. Maximum Product Subarray 解题思路

  • 这一题的解题流程与上一题基本类似,但是要解决的关键问题是:状态转移,即如何根据上一个子问题(以第 j 个元素为结尾的子段的max product)的答案推算出当前子问题的结果。
  • 从上一题的分析可以看出,当前子问题(以第 j 个元素为结尾的子段的max sum)的计算只需考虑上一个子问题的结果 $sum[j-1]$,$sum[j-1] < 0$,因为是加法,显然可以将子问题结果忽略;$sum[j-1] > 0$,$sum[j-1]$ 加上当前元素就是当前子问题的结果。
  • 类似的问题,只不过换成乘积,子问题的求解就变得复杂了,需要考虑以下几种情况:
    • 当前元素是正数,max product可能是正正得正的情况,因为都是整数,乘积>1,上一子问题的结果乘上当前元素即为当前子问题的答案
    • 当前元素是负数,max product可能是负负得正的情况,因此需要维护以第 j 个元素为结尾的子段的min product(很大可能是负数)
    • 另外,需要考虑上一个子问题的结果为0的情况
    • 总之,乘积的最大值为上述三种情况之一
      状态转移方程如下:
      $$ maxProducts[j+1] = max(maxProducts[j-1]*nums[j], minProducts[j-1]*nums[j], nums[j])$$
  • 152. Maximum Product Subarray 解答如下:
123456789101112131415161718192021222324252627282930
class Solution {public:    int maxProduct(vector<int>& nums) {        int SizeofNums = nums.size();

if(SizeofNums == 1) {            return nums[0];        }        // The largest/least product of subarray ending with the i-th element        int maxProducts[SizeofNums];        int minProducts[SizeofNums];        maxProducts[0] = minProducts[0] = nums[0];        for(int i=1; i<SizeofNums; i++) {            // positive with positive, negative with negative, ignore previous zero            maxProducts[i] = max( max(maxProducts[i-1]*nums[i], minProducts[i-1]*nums[i]), nums[i]);            // positive with negative, negative with positive, ignore previous zero            minProducts[i] = min( min(maxProducts[i-1]*nums[i], minProducts[i-1]*nums[i]), nums[i]);        }

// getting the largest product for the whole array        int largestProduct = maxProducts[0];        for(int i=1; i<SizeofNums; i++) {            if(maxProducts[i] > largestProduct) {                largestProduct = maxProducts[i];            }        }

return largestProduct;    }};
  • 与上一题类似,添加额外变量,也能实现节省内存,记录子段最大乘积对应子段($nums[startIdx, endIdx]$)的起始和终止位置。
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849
class Solution {public:    int maxProduct(vector<int>& nums) {        int SizeofNums = nums.size();

if(SizeofNums == 1) {            return nums[0];        }        // The largest/least product of subarray ending with the i-th element        int largestProduct = nums[0];        int leastProduct = nums[0];        // The largest product for the whole array        int maxProduct = largestProduct;        // subarray[startIdx, endIdx] with largest product for the whole array        int startIdx = 0, endIdx = 0;        // start index for largestProduct/leastProduct        int startIdx_pos = startIdx, startIdx_neg = startIdx;

for(int i=1; i<SizeofNums; i++) {            int largestProduct_pre = largestProduct;            int leastProduct_pre = leastProduct;

// positive with positive, negative with negative, ignore previous zero            largestProduct = max( max(largestProduct_pre*nums[i], leastProduct_pre*nums[i]), nums[i]);            if((largestProduct_pre != nums[i]) && (largestProduct == nums[i])) {                startIdx_pos = i;            }

// positive with negative, negative with positive, ignore previous zero            leastProduct = min( min(largestProduct_pre*nums[i], leastProduct_pre*nums[i]), nums[i]);            if((leastProduct_pre != nums[i]) && (leastProduct == nums[i])) {                startIdx_neg = i;            }

if(largestProduct > maxProduct) {                maxProduct = largestProduct;                if(largestProduct_pre*nums[i] > leastProduct_pre*nums[i]) {                    startIdx = startIdx_pos;                }                else {                    startIdx = startIdx_neg;                }                endIdx = i;            }        }

return maxProduct;    }};

原文地址:https://www.cnblogs.com/petewell/p/11408859.html

时间: 2024-10-13 23:21:23

算法设计与分析[0009] Dynamic Programming(II)(Maximum Sum/Product Subarray)的相关文章

(转)常用的算法设计与分析-一夜星辰的博客

算法设计与分析 分治法 思想 1. 将一个规模为n的问题分解为k个规模较小的子问题,这些子问题互相独立且与原问题相同.递归地解这些子问题,然后将各子问题的解合并得到原问题的解. 2. divide-and-conquer(P) { if(|P| <= n0)adhoc(P); divide P into samller subinstances P1,P2...,Pk; for(int i = 1;i < k;i++) { yi = divide-and-conquer(Pi); } retu

算法设计与分析 ------最近对问题与8枚硬币问题

利用减治法实现8枚硬币问题: 参考资料:http://blog.csdn.net/wwj_748/article/details/8863503    算法设计--八枚硬币问题 1 #include "stdafx.h" 2 #include <iostream> 3 #include <stdio.h> 4 using namespace std; 5 6 7 void eightcoin(int arr[]); 8 void compare(int a,in

《计算机算法设计与分析》v4 第1章 算法概述 算法实现题答案

博主今年刚上大三,正好开算法这门课.由于博主本人比较喜欢算法但又比较懒,啃不动算法导论,所以决定拿这本书下手. 这本书是王晓东的第四版<计算机算法设计与分析>.初步打算将每章后面的算法题都用代码实现. 有些题跟某个ACM题目很像,我会把该ACM题的链接贴上.有的题没OJ交所以可能是错的.如有发现,还望指出. 1-1 统计数字问题 http://poj.org/problem?id=2282 这个题要按位分解,一位一位的来处理. #include<iostream> #include

【通知】《算法设计与分析》实验课、理论课补课、考试时间、加分等安排 及 个人目标设定

Logistic回归为概率型非线性回归模型,是研究二分类观察结果与一些影响因素之间关系的一种多 变量分析方法.通常的问题是,研究某些因素条件下某个结果是否发生,比如医学中根据病人的一些症状来判断它是 否患有某种病. 在讲解Logistic回归理论之前,我们先从LR分类器说起.LR分类器,即Logistic Regression Classifier. 在分类情形下,经过学习后的LR分类器是一组权值,当测试样本的数据输入时,这组权值与测试数据按 照线性加和得到 这里是每个样本的个特征. 之后按照s

算法设计与分析基础(第3版)读书笔记(及几处翻译上的错误~~)

算法设计与分析基础(第3版) p16 in-place翻译为'在位'?'就地'更合适点 p38 amortized应翻译为'均摊','摊销'这个词简直莫名其妙(可能因为翻译是做算法交易导致的?) p64 迭代优于递归(迭代始终是增量式的,而递归就没办法增量了,除非能够dump整个运行时栈) p73 通过算法可视化得到一个更好的非递归算法(人的图像认知直觉思维?) p79 验证一个拓扑是环.星.还是团?(这个地方有点意思,因为我想到了动态的Verify) p87 凸包问题:从数据结构上讲,Set<

算法设计与分析(屈婉玲)pdf

下载地址:网盘下载 算法设计与分析本教材为计算机科学技术专业核心课程"算法设计与分析"教材.<算法设计与分析>以算法设计技术和分析方法为主线来组织各知识单元,主要内容包括基础知识.分治策略.动态规划.贪心法.回溯与分支限界.算法分析与问题的计算复杂度.NP完全性.近似算法.随机算法.处理难解问题的策略等.书中突出对问题本身的分析和求解方法的阐述,从问题建模.算法设计与分析.改进措施等方面给出适当的建议,同时也简要介绍了计算复杂性理论的核心内容和处理难解问题的一些新技术. &

算法设计与分析——回溯法算法模板

以深度优先方式系统搜索问题解的算法称为回溯法.在回溯法中,解空间树主要分为了四种子集树.排列树.n叉树和不确定树. 在<算法设计与分析课本>中介绍了11个回溯法的问题样例,这里根据解空间树的类型做一个分类. 子集树 装载问题 符号三角形问题 0-1背包问题 最大团问题 算法模板: void backtrack(int t) { if(搜索到叶子结点) { return; } for(i=0; i<=1; i++) //01二叉树 { if(满足约束函数和限界函数)//剪枝 { backt

算法设计与分析-Week12

题目描述 You are given coins of different denominations and a total amount of money amount. Write a function to compute the fewest number of coins that you need to make up that amount. If that amount of money cannot be made up by any combination of the c

算法设计与分析课程复习笔记(1)

一.计算模型 1.1 定义: 我们在思考和处理算法的时候是机器无关.实现语言无关的.所有的算法运行在一种"抽象的机器"之上,这就是计算模型. 1.2 种类 图灵机是最有名的计算模型,本课使用更简单更合适的RAM计算模型. 1.3 RAM(Random Access Machine)模型 RAM模型的基本构成如下: RAM计算模型有如下特点: 一个简单操作花费一步:键值比较.加减.内存访问 没有操作可以被分解:循环.子程序 内存:访存是一个简单操作.无限制的内存 二.算法设计 2.1 算