最大子序列和
dp方法,联机法 【O(N)】
记录 i 之前的所有和sum,保留前面i-1项中sum最大的值;
当sum<=0时,证明前面的i-1项不会为后面带来增值,舍去。
令sum=0,从 i 重新开始。
int MaxSonString(int a[], int N)
{
int MaxSum=0;
int tempSum = 0;
int low=0,low2=0,high = 0;//记录位置,low2储存重新开始的位置
for (int i = 0; i < N; i++)
{
tempSum += a[i];
if (tempSum > MaxSum)
{
MaxSum = tempSum;
low = low2;
high = i;
}
else if(tempSum<0)
{
tempSum = 0;
low2 = i+1;//重新开始
}
}
//output(a, low, high);
return MaxSum;
分治法 【O(NlogN)】
三个地方可能出现最大序列和
1、左边子序列
2、右边子序列
3、中间部分最大和=左边包含最右元素最大和+右边包含最左元素最大和
max(左,右,中)
int MaxSonSum(int a[], int left, int right)
{
int MidSonSum = 0;
int leftSonSum = 0, rightSonSum = 0;
int MidSonleftSum = 0, MidSonrightSum = 0;
int MidSonleftSum2 = 0, MidSonrightSum2 = 0;
//递归的出口
if (left == right)
return a[left];
int center = (right + left) / 2;
leftSonSum = MaxSonSum(a, left, center);
rightSonSum = MaxSonSum(a, center+1,right);
//中间部分
for (int i = center; i >= left; i--)
{
MidSonleftSum2 += a[i];
if (MidSonleftSum2 > MidSonleftSum)
MidSonleftSum = MidSonleftSum2;
}
for (int i = center+1; i <= right; i++)
{
MidSonrightSum2 = +a[i];
if (MidSonrightSum < MidSonleftSum2)
MidSonrightSum = MidSonrightSum2;
}
MidSonSum = MidSonleftSum + MidSonrightSum;
//return Max(leftSonSum,rightSonSum,MidSonSum);
if (leftSonSum > rightSonSum)
{
if (leftSonSum > MidSonSum)
return leftSonSum;
else
return MidSonSum;
}
else
{
if (rightSonSum > MidSonSum)
return rightSonSum;
else return MidSonSum;
}
}
最小序列和
dp 联机法
与最大序列和方法相同,差别是找到最小,当sum>=0时,舍去前面。
int MinSonString(int a[], int N)
{
int MinSum = 0;
int tempSum = 0;
int low = 0, low2 = 0, high = 0;//记录位置,low2储存重新开始的位置
for (int i = 0; i < N; i++)
{
tempSum += a[i];
if (tempSum < MinSum)
{
MinSum = tempSum;
low = low2;
high = i;
}
else if (tempSum > 0)
{
tempSum = 0;
low2 = i+1;//重新开始
}
}
// output(a, low, high);
return MinSum;
}
最小正序列和
1、生成一个结构体( sum,num );其中num是前i个,sum是前i个的和
(所有序列均可由 numN-numM 得出)
2、对sum的大小对(结构体)数组进行排序
3、比较得到最小正序
正确条件:b[i].num>b[i-1].num && b[i]sum>b[i-1].sum && b[i].sum-b[i-1].sum<当前最小正序和。
- eg a[6] = { 4,1,-3,9,-10,5}
- 将序列分为 {4},{4,1},{4,1,-3},{4,1,-3,9},{4,1,-3,9,-10},{ 4,1,-3,9,-10,5}
- 所有序列的情况可 有上面的序列相减获得。
- 记录序列的sum,和 秩(位数) ,对sum其排序。【node {value,num}】
- 最小正序列 = 排序后的后一位减去前一位的值最小者且大于0
struct node
{
int value;
int num;
};
int MinTrueSonSum(int a[], int N)
{
node b[max];//
int sum = 0;
//初始化 结构体
for (int i = 0; i < N; i++)
{
sum += a[i];
b[i].value = sum;
b[i].num = i;
}
QSort(b, 0, N-1);//快排. value从小到大
int min = b[0].value>=0?b[0].value:b[N-1].value;
for (int i = 1; i < N; i++)
{
//可能出现:后一列比前一列多项
if (b[i].num > b[i - 1].num )
{
int temp = b[i].value - b[i - 1].value;
if (temp > 0 &&(temp < min || min <= 0)))
//(temp<min || min<=0)保证当b[].value都<0时也正确更新值
{
min = temp;
}
}
}
return min;//如果min<=0 则找不到最小子列正序和
}
//------------------------快排-----------------------//
int quicksort(node a[], int low, int high)
{
//保留第一个位置
node pos = a[low];
while (low<high)
{
while (low<high && a[high].value>=pos.value) --high;
a[low] = a[high];
while (low<high && a[low].value<=pos.value) ++low;
a[high] = a[low];
a[low] = pos;
}
return low;//中间位置
}
int QSort(node a[], int low, int high)
{
if (low < high)
{
int temp = quicksort(a, low, high);
QSort(a, low, temp);
QSort(a, temp + 1, high);
}
return 0;
}
子列最大乘积
dp 联机法
- 使用联机法:当a[i]=0时,清空pre_max和pre_min
- 到i的最大cur_max=max(pre_numa[i],pre_mina[i],a[i])
- 到i的的最大cur_min=min(pre_numa[i],pre_mina[i],a[i])
- 保留前i项的最大 Max=max(Max,cur_max);
- 更新pre_max,pre_min
int Maxproduct(int a[], int N)
{
int Max = a[0];
int pre_max = a[0], pre_min = a[0];
int cur_max = a[0], cur_min = a[0];
for (int i = 1; i < N; i++)
{
//得到当前最大最小值;为0时会自动清为0,等到下一个a[i]重新赋值
cur_max = max(pre_max * a[i], pre_min * a[i], a[i]);
cur_min = min(pre_max * a[i], pre_min * a[i], a[i]);
Max = max(cur_max, Max, 0);
pre_max = cur_max;
pre_min = cur_min;
}
return Max;
}
负数的奇偶个数
- 关于负数的奇偶个,可以快速得到最大和。记录负数的个数
- 偶数,则全部相乘最大
- 奇数,则可能为:
** 1、第一个奇数之后的累乘。 (使用标志 累乘到第一个负数的left_mul)
** 2、最后一个奇数之前的累乘。(累乘的最后一个负数的right_mul) - a[i]=0时,重置 当前乘积cur_max,标志,left_mul,right_mul,负数个数。
int MAXproduct(int a[], int N)
{
int num = 0;//记录奇偶个
int Max = 1;
int cur_max = 1;
int left_mul = 1;//记录到第一个奇数的乘积。
int right_mul = 1;//记录到最后一个奇数的乘积。
bool first = 0;
for (int i = 0; i < N; i++)
{
if (a[i] == 0)//重置
{
right_mul = left_mul = 1;
first = 0;
cur_max = 1;
num = 0;
}
else
{
if (a[i] < 0)
{
if (!first)
{
//得到第一个奇数左边的乘积
left_mul *= a[i];
first = 1;
}
//当前最后一个奇数的前面的乘积
right_mul = cur_max;
num++;
}
cur_max = cur_max * a[i];
//更新Max值
if (num % 2 == 0 && Max < cur_max)
Max = cur_max;
else //取最大值,奇数个时
Max=max(Max,right_mul,cur_max/left_mul);
}
}
return Max;
}