最长公共子序列问题

问题描述:

给定整数A1,A2,…,AN(可能有负值),求连续子序列和的最大值。(为方便起见,如果所有整数都为负值,则最大子序列和为0)

  • 方法一:

这是个显而易见的方法,几乎每个人在第一眼看到该问题都能够想出来的方法。就是将所有的子序列找出来,然后求和最大的一个。如果序列足够大,该方法的效率可想而知。

代码如下:

#include <iostream>
#include <vector>

using namespace std;

typedef vector<int> Vect;
int maxSubSum1(const Vect &ivec, int &begin, int &end);//begin和end带回最大子序列的起点和终点

int main(void)
{
    const int a[] = {1, 2, -5, 3, 4};
    Vect ivec(a, a + sizeof(a)/sizeof(int));
    int begin, end;

    int maxSum = maxSubSum1(ivec, begin, end);
    cout << maxSum << endl;
    if(begin == 0 && end == 0)
    {
	cout << "最大子序列和为负数。" << endl;
    }
    else
    {
	cout << "是从第" << begin << "个元素到第" << end << "个元素。" << endl;
    }

    system("pause");
    return 0;
}

int maxSubSum1(const Vect &ivec, int &begin, int &end)//begin和end带回最大子序列的起点和终点
{
    int maxSum = 0;//在题目中,若做大子序列和为负数,则结果为0

    begin = 0, end = 0;//若结果为负数,则begin和end都为0
    for(Vect::size_type i = 0; i != ivec.size(); ++i)
    {
	int sum = 0;
	for(Vect::size_type j = i; j != ivec.size(); ++j)
	{
            sum += ivec.at(j);//累加得到每个子序列
	    if(sum > maxSum)
	    {
		maxSum = sum;
		begin = i+1;//begin的最小值应为1
		end = j+1;  //end的最小值也应为1
	    }
	}
    }

    return maxSum;
}


 
 
  • 方法二:

采用分治的方法,将这个序列分为大小相等的两个子序列,其最大子序列和必然为:前一个子序列中的最大子序列、后一个子序列中的最大子序列和前半部分最大和(包括前半部分的最后一个元素)加上后半部分最大和(包括后半部分的第一个元素)这三者之一。

代码如下:

#include <iostream>
#include <vector>

using namespace std;

typedef vector<int> Vect;
typedef vector<int>::size_type SizeT;
int maxSubSum2(Vect &ivec, SizeT left, SizeT right);

int main(void)
{
    int a[] = {1, 2, -5, 3, 4};
    Vect ivec(a, a + sizeof(a)/sizeof(int));

    int maxSum = maxSubSum2(ivec, 0, ivec.size()-1);
	
    cout << maxSum << endl;

    system("pause");
    return 0;
}

int maxSubSum2(Vect &ivec, SizeT left, SizeT right)
{
    if(left == right)//只有一个元素的情况
    {
	if(ivec[left] > 0)
	{
            return ivec[left];
	}
	else
	{
	    return 0;
	}
    }

    SizeT mid = (left + right) / 2;
    int leftMaxSum = (ivec, left, mid);
    int rightMaxSum = (ivec, mid+1, right);
	
    int leftBorderMaxSum = 0, leftBorderSum = 0;
    for(SizeT i = mid; i >= left; --i)
    {
	leftBorderSum += ivec[i];
	if(leftBorderSum > leftBorderMaxSum)
	{
            leftBorderMaxSum = leftBorderSum;
	}

	/*i为unsigned int型,无论如何都不可能小于0,
	 *如果没有这个if语句,怎么都无法跳出循环,程
	 *序崩溃
	 */
	if(i == 0)
	    break;
    }
	
    int rightBorderMaxSum = 0, rightBorderSum = 0;
    for(SizeT i = mid+1; i <= right; ++i)
    {
	rightBorderSum += ivec[i];
	if(rightBorderSum > rightBorderMaxSum)
	{
            rightBorderMaxSum = rightBorderSum;
	}
    }

    if(leftMaxSum > rightMaxSum)
    {
	return leftMaxSum > (leftBorderMaxSum + rightBorderMaxSum) ?
	   leftMaxSum : (leftBorderMaxSum + rightBorderMaxSum);
    }
    else
    {
	return rightMaxSum > (leftBorderMaxSum + rightBorderMaxSum) ?
	   rightMaxSum : (leftBorderMaxSum + rightBorderMaxSum);
    }
    return 0;
}
  • 方法三:

采用动态规划的思想。

代码如下:

#include <iostream>
#include <vector>

using namespace std;

typedef vector<int> Vect;
typedef vector<int>::size_type SizeT;
int maxSubSum3(const Vect &ivec, int &begin, int &end);//begin和end带回最大子序列的起点和终点

int main(void)
{
    int a[] = {1, -2, 2, 3, 4};
    Vect ivec(a, a + sizeof(a)/sizeof(int));
    int begin, end;

    int maxSum = maxSubSum3(ivec, begin, end);
	
    cout << maxSum << endl;
    if(begin == 0 && end == 0)
    {
        cout << "最大子序列和为负数。" << endl;
    }
    else
    {
        cout << "是从第" << begin << "个元素到第" << end << "个元素。" << endl;
    }

    system("pause");
    return 0;
}

int maxSubSum3(const Vect &ivec, int &begin, int &end)//begin和end带回最大子序列的起点和终点
{
    int maxSum = 0;
    int temp = 0;

    begin = 0, end = 0;
    for(SizeT i = 0, tag = 0; i < ivec.size(); ++i)
    {
	temp += ivec[i];
	if(temp > maxSum)
	{
            maxSum = temp;
	    begin = tag+1;
	    end = i+1;
	}
	if(temp < 0)
	{
	    temp = 0;
	    tag = i+1;
	}
    }

    return maxSum;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值