开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第1天,点击查看活动详情
题目描述
给定整型数组,求最大子序列和(单串动态规划)
//输入
nums={-2,1,-3,4,-1,2,1,-5,4}
//输出
6
思路分析
动态规划
基本描述
动态规划解决问题最优解,将原问题划分为若干个子问题,找出最优子结构,进而得出原问题的最优解。注意:当子问题之间存在相同数据的重复计算时可以用动态规划来解决,以便提升代码效率,当子问题间不存在相同数据的重复计算时,可以使用普通递归
f(5)=f(4)+f(3)
f(4)=f(3)+f(2)
//此时重复了f(3)的计算,可以使用动态规划,以便效率提升
难点
- 理解问题,定义f(n)
- 通过f(1)....f(n-1)推出f(n),即确定具体的状态方程
具体思路
通过索引值依次遍历数组内的各个值,sum记录访问数组值的和,当访问过的数值和小于当前值的和时,前面的便不再是最大和,接下来从当前值开始记录和;当访问过的数值和大于当前值的和时,记录访问过的数值和+当前值。即,当前最大序列和=max(上一元素最大和,上一元素最大和+当前值)。即当前最优解=max(上一问题最优解,上一问题最优解+当前解)。
//方法实现,此方法不记录最大值索引
int maxSubArray(vector<int> &nums){
int len=nums.size();
vector<int> largest(len);
largest[0]=nums[0];
for(int i=1;i<len;i++){
largest[i]=max(largest[i-1]+nums[i],nums[i]);
}
return *max_element(largest.begin(),largest.end());
}
//改进,记录最大值索引,返回最大值坐标
vector<int> maxSubArray(vector<int>& nums) {
//使最大值等于最小值常量(不能为0,因为数组中可能都是负数)
int maxsum=INT_MIN,len=nums.size();
//用来记录最大值坐标
vector<int> ans(2);
//记录开始位置
int begin = 0;
//sum记录每个子段的和
int sum=nums[0];
for(int i=1;i<len;i++){
//新sum=原sum+当前值,当原sum<0,此时新sum=原sum+当前值<当前值,为了取最大,从当前值开始计算,此时新sum=当前值;如果原sum>0,新sum=原sum+当前值>当前值
//sum>0时
if(sum>0)
sum+=nums[i];
//sum<0时
else{
//此时sum从位置i的值开始记录,即将nums[i]赋值到sum
sum=nums[i];
//当nums[i]自立门户时候,我们记录下子序列的起始位置
begin=i;
}
if(sum>maxsum){//更新答案
//依次记录最大和
maxsum=sum;
ans[0]=begin;//记录下起始和终止位置
ans[1]=i;
}
}
return ans;
}
};
求最大子数组乘积
//负数最小乘以负数是最大,正数最大乘以正数是最大
//输出较大值里面的最大值
//关键代码
for(int i=1;i<len;i++){
large[i]=max(nums[i],max(large[i-1]*nums[i],small[i-1]*nums[i]));
small[i]=min(nums[i],min(large[i-1]*nums[i],small[i-1]*nums[i]));
}
return *max_element(large.begin(),large.end());
知识点补充
max_element()函数:
vector<int> v;
*max_element(v.bedin(),v.end());
//求出vector容器的最大值,将容器从小到大排序,输出最后一个,也就是最大值
*max_element(v.bedin(),v.end());
//求出vector容器的最小值,将容器从小到大排序,输出第一个,也就是最小值
v.begin() //指向迭代器中第一个元素。
v.end() //指向迭代器中末端元素的下一个,指向一个不存在元素。
v.rbegin() //传回一个vector的最后一个数据的指针。
v.rend() // 传回一个vector的第一个数据前一个位置的指针
v.back() //获取vector容器的最后一个元素
v[v.size()-1] //获取vector容器的最后一个元素