目 录
最小子序列和
int minSubSum2(const vector<int> &arr)
{
int minSum = 0;
for(int i=0;i<arr.size();i++){
int thisSum = 0;
for(int j=i;j<arr.size();j++){
thisSum+=arr[j];
if(thisSum < minSum){
minSum = thisSum;
}
}
}
return minSum;
}
int minSubSum4(const vector<int> &arr)
{
int thisSum = 0;
int minSum = 0;
for(int i=0;i<arr.size();i++){
thisSum += arr[i];
if(thisSum < minSum){
minSum = thisSum;
}else if(thisSum > 0){//任何正的子序列不可能是最小子序列的前缀
thisSum = 0;
}
}
return minSum;
}
最小的正子序列和
/*
*计算最小的正子序列之和
*/
int minPositiveSubSum(const vector<int> &arr){
int sum = 0;
int flag = 0;
int res;
vector<int> a;
for(int i=0;i<arr.size();i++){
sum+=arr[i];
a.push_back(sum);
}
sort(a.begin(),a.end());//默认从小到大排序
for(int i=0;i<a.size();i++){
if(a[i]-a[i-1]>0){
if(flag == 0){
res = a[i]-a[i-1];
flag =1;
}else{
if(a[i]-a[i-1] < res)
res = a[i] - a[i-1];
}
}
}
return res;
}
思路:
虽然说遇到连续和会想到前缀和,但是这道题对前缀和的使用实在是太巧妙了! Orz大佬的思想:
我们记录前缀和的位置pos,以及前缀和的值val
然后对前缀和的值进行排序
然后我们找相邻的点,如果满足前面的pos小于后面的pos,它们 val 的差大于0 那么我们就记录它们的val之差
为什么是找相邻的点就可以了呢?
解释一下为什么只需检查相邻2个数就可以,设ABC是排序后的结果,如果A同B不能组成序列,而A同C可以组成序列,那么B同C也可以组成序列,并且BC会是一个更优的解。
略微证明下:
因为A和B不能组成序列,那么B的pos 小于 A的pos。 因为A和C可以组成序列,那么C的pos 大于 A的pos
所以B和C肯定也可以组成序列!因为又是排序过了的,所以B和C的差值肯定是优于A和C的差值的
最大子序列乘积
用了动态规划,乘积不像加法,两个负数相乘可能会是最大的
算法1:
这个代码有点问题,但思路正确
maxNeg = -maxPos*a[i];
maxPos = -maxNeg*a[i];// maxNeg == 0 will cause an empty positive suffix这里赋值有点问题
int maxSubProd(const vector<int> &a)
{
int maxVal = 1, maxPos = 1, maxNeg = 0;
for (auto i = 0; i < a.size(); i++){
if (a[i] > 0){
maxPos = maxPos*a[i];
maxNeg = maxNeg*a[i];
}else if (a[i] < 0){
maxNeg = -maxPos*a[i];
maxPos = -maxNeg*a[i];// maxNeg == 0 will cause an empty positive suffix
}else{
maxPos =1;
maxNeg = 0;
}
cout<<"此时i为"<<i<<" "<<"maxPos="<<maxPos<<" "<<"maxNeg="<<maxNeg
<<endl;
if (maxPos < 1) maxPos = 1;
if (maxPos > maxVal) maxVal = maxPos;
}
return maxVal;
} // O(n)
算法2
该做法转别人的,还不是很懂 链接地址是原文
#define N 20000
int maxSubMul(const vector<int> &arr){
int maxArr[N],minArr[N];
maxArr[0] = minArr[0] = arr[0];
int maxMul = arr[0];
for(int i=1;i<arr.size();i++){
maxArr[i] = max(max(arr[i],minArr[i-1]*arr[i]),maxArr[i-1]*arr[i]);
minArr[i] = min(min(arr[i],minArr[i-1]*arr[i]),maxArr[i-1]*arr[i]);
maxMul = max(maxMul,maxArr[i]);
}
return maxMul;
}
核心: 每一步只需要记住其前一步的正数最大值和负数的最小值
举个例子:
maxA[i-1]=3
minA[i-1]=-2
此时的Ai=-1
由此可得
maxA[i+1]=-2*-1=2
minA[i-1]=3*-1=-3
根据这个例子便可推出关于maxA[i],minA[i]的方程即
maxA[i] = max(max(a[i], minA[i - 1] * a[i]), maxA[i - 1] * a[i]);
minA[i] = min(min(a[i], minA[i - 1] * a[i]), maxA[i - 1] * a[i]);
算法3:
int maxSubMul4(const vector<int> &arr)
{
int thisMul = 1;
int maxMul = 1;
for(int i=0;i<arr.size();i++){
thisMul *= arr[i];
if(thisMul > maxMul){
maxMul = thisMul;
}else if(thisMul < 0){
thisMul = 1;
}
}
return maxMul;
}
主程序
int main()
{
vector<int> arr1 = {4,-1,5,-2,-1,2,6,-2,-1,5,-2,-1};
vector<int> arr2 = {9,-3,-5,-7,11,7};
vector<int> arr3 = {2,-3,-3,7,5};
vector<int> arr4 = {3,-2,5,-7,9,-6,-7,-3,2,10};
cout<<"sum is (minSubSum4) "<<minSubSum4(arr1)
<<" "<<minSubSum4(arr2)
<<" "<<minSubSum4(arr3)
<<" "<<minSubSum4(arr4)<<endl; //正确
cout<<"sum is (minPositiveSubSum) "<<minPositiveSubSum(arr1)
<<" "<<minPositiveSubSum(arr2)
<<" "<<minPositiveSubSum(arr3)
<<" "<<minPositiveSubSum(arr4)<<endl; //正确
// cout<<"sum is (minPosSubSum) "<<minPosSubSum(arr1)
// <<" "<<minPosSubSum(arr2)
// <<" "<<minPosSubSum(arr3)
// <<" "<<minPosSubSum(arr4)<<endl; //错误
cout<<"mul is (maxSubMul) "<<maxSubMul(arr1)
<<" "<<maxSubMul(arr2)
<<" "<<maxSubMul(arr3)
<<" "<<maxSubMul(arr4)<<endl; //结果正确
cout<<"mul is (maxSubProd) "<<maxSubProd(arr1)
<<" "<<maxSubProd(arr2)
<<" "<<maxSubProd(arr3)
<<" "<<maxSubProd(arr4)<<endl; //错误
cout<<"mul is (maxSubMul4) "<<maxSubMul4(arr1)
<<" "<<maxSubMul4(arr2)
<<" "<<maxSubMul4(arr3)
<<" "<<maxSubMul4(arr4)<<endl; //错误
return 0;
}
结果
sum is (minSubSum4) -3 -15 -6 -16
sum is (minPositiveSubSum) 1 1 1 1
mul is (maxSubMul) 9600 2695 630 793800
mul is (maxSubProd) 76800 7640325 5670 840157920
mul is (maxSubMul4) 12 77 35 20
按 <RETURN> 来关闭窗口...
^C
最小正子段和 - _Ackerman - 博客园 求最小子序列和 求最小的正序列 求最大子序列乘积_qq789045的博客-CSDN博客 最小正子段和 - _Ackerman - 博客园