子序列和问题

最大子序列和

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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值