动态规划-最大子序列和

今天闲着没事,看了网上一篇文章关于最大子序列的问题,看了算法,自己实现了一下。

所谓最大子序列和是指一个数组所有连续子序列中,和最大的那一组。比如 -1,2,-4,5 的最大子序列是5.  2,-1,3,2,-5的最大子序列为 2,-1,3,2.

动态规划,目前我理解的就是如果想解决一个大问题,先把该大问题分成几个小问题,然后再继续分解直到能解决为止,但是某些小问题的结果可能会被多次用到,所以我们可以把每个结果都保存起来,这样就不用每次用到都去重新计算。

对于本题,我们假设 f(i)为以第i个元素结尾的最大子序列的和。比如 2,-3,5,2 f(0)=2,f(1)=-1,f(2)=5,f(3)=7。注意必须是以i结尾的这样f(1)=-1而不是2。

下面分析f(i+1)的值,f(i+1)是以a[i+1]结尾的,所以f(i+1)=f(i)+a[i+1],或者f(i+1)=a[i+1]。因为f(i)有可能是负的,所以f(i+1)有可能是a[i+1]。

所以我们可以把每个f(i)的值都计算出来,因为,最大和子序列肯定是以某个值结尾的,然后取最大值就可以了。

下面写一下代码

View Code
#include <stdio.h>
#include<stdlib.h>

int *num;
int Method(int i);
int Sum=0;
int main(void)
{
    
    int n=0;
    scanf("%d",&n);
    num=(int *)malloc(n*sizeof(int));
    
    for(int i=0;i<n;i++)
    {
        scanf("%d",&num[i]);
        
    }
    Method(n-1);
    printf("%d",Sum);
}

int Method(int i)
{
    if(i==0)
    {
        Sum=num[0];
        return num[0];
    }
    else
    {
        int temp=Method(i-1);
        int ret;
        if(temp>0)
        {
            ret=temp+num[i];
        }
        else 
        {
            ret=num[i];
        }
        if(ret>Sum)
            Sum=ret;
        return ret;
    }
}

由于每个f(i)只会记录一次,所以不用记录每个值,但是如果会多次用到则需要记录下来,比如菲比那切数列,f(i)=f(i-1)+f(i-1)=f(i-2)+f(i-3)+f(i-1),可以看出每个值会多次用到,所以我们可以开辟一个大小为n的数组存储每个值。此例不用。

下面代码是可以记录最长子序列下标的。

 

View Code
#include <stdio.h>
#include<stdlib.h>
int *num;
int Method(int i,int &l,int &r);
int Sum;
int left;
int right;
int main(void)
{
    int n=0;

    scanf("%d",&n);
    num=(int *)malloc(n*sizeof(int));

    for(int i=0;i<n;i++)
    {
        scanf("%d",&num[i]);

    }
    int l,r;
    Method(n-1,l,r);

    printf("最长子序列%d 起始坐标%d 终止坐标%d \n",Sum,left,right);

    printf("\n");

    return 0;
}


int Method(int i,int &l,int& r)
{
    if(i==0)
    { 
        Sum=num[0];
        l=0;
        r=0;
        left=0;
        right=0;
        return num[0];
    }
    else 
    {

        int temp=Method(i-1,l,r);
        int ret;
        if(temp>0)
        {
            ret= temp+num[i];
            l=l;
            r=i;
        }
        else
        {
            ret=num[i];
            l=i;
            r=i;
        }

        if(ret>Sum)
        {
            Sum=ret;
            left=l;
            right=r;
        }
        return ret;

    }

}

其中left right记录的是最大的那个序列的左下标和右下标。l 和r两个引用是为了能获得f(i-1)的序列的左下标和右下标,由于只能有一个返回值,被Sum用了,所以用引用来把下标传回来

 

 注:转载请注明出处 http://www.cnblogs.com/ITPuppy/archive/2013/04/16/3024058.html

posted on 2013-04-16 22:49  ITailor  阅读(552)  评论(0)    收藏  举报

导航