7-1 最大子列和问题 (20分)(两种解法,包含联机算法)

7-1 最大子列和问题 (20分)

给定K个整数组成的序列{ N​1​​ , N​2​​ , …, N​K },“连续子列”被定义为{ N​i, N​i+1 , …, N​j },其中 1≤i≤j≤K。“最大子列和”则被定义为所有连续子列元素的和中最大者。例如给定序列{ -2, 11, -4, 13, -5, -2 },其连续子列{ 11, -4, 13 }有最大的和20。现要求你编写程序,计算给定整数序列的最大子列和。

本题旨在测试各种不同的算法在各种数据情况下的表现。

输入格式:
输入第1行给出正整数K (≤100000);第2行给出K个整数,其间以空格分隔。

输出格式:
在一行中输出最大子列和。如果序列中所有整数皆为负数,则输出0。

输入样例:
6
-2 11 -4 13 -5 -2
输出样例:
20

这题一共有四种解法(在《数据结构与算法分析—c语言版》p21开始)

这里我就写了两种,因为我觉得这两种好一点也许吧
有一说一也是确实好,写完可以去PTA这题交一发
(题目传送门:https://pintia.cn/problem-sets/15/problems/709)
第一种会比第二种更好理解,但第二种代码更简短运行时间也更少(写出来的两种都比书上另外两种要快)

第一种
三个自定义函数,主要看第二个,第一个就是找Max前半段sum,Max后半段sum,前后半段maxsum,这三个数的最大值,第二个里面有一点点递归不难理解的 虽然我看了一会 ,最后一个函数就是让最后用起来更方便,要是不想写,直接第二个进主函数也是可以用的(就直接Maxsubsum(a,0,n-1))

看代码吧

            #include
            #include
            #include
            #include
            #include
            #include
            #include
            using namespace std;
            typedef long long ll;
            int a[100000];
    int max3(int z,int x,int v)   //找最大
    //(我这种找最大的写法会warning,大概意思就是警告我这个函数可能会没有返回值,但其实没事,这个函数已经判断了全部情况)
    //不想用的也有其他写法(我写在下面了)
    {
       if(z>=x&&z>=v)  
        return z;
       if(x>=z&&x>=v)
        return x;
       if(v>=z&&v>=v)
        return v;
    }
    /*
    int max3(int z,int x,int v)   //这就是第二种找Max的
    {
    int max=z;
    if(x>max)
    max=x;
    if(v>max)
    max=v;
    return max;
    }
    
    */
     int Maxsubsum(int a[],int left,int right)  
     {
          int maxleftsum,maxrightsum;   
          int maxleftbordersum,maxrightbordersum;
          int leftbordersum,rightbordersum;
          int center,i;
          center=(left+right)/2;  //找中间位置
          if(left==right)   //相同就代表只有一个元素
            if(a[left]>0)  //题目只要大于0(a[left]==a[right]所以用哪个都一样)
                return a[left];
            else
                return 0;
          maxleftsum=Maxsubsum(a,left,center);   //递归(套几个数据跟着一起走大概能明白)
          maxrightsum=Maxsubsum(a,center+1,right);

          maxleftbordersum=0;
          leftbordersum=0;
          for(int i=center;i>=left;i--)   //找左半部分最大
          {

              leftbordersum+=a[i];
              if(leftbordersum>maxleftbordersum)
                maxleftbordersum=leftbordersum;
          }
          maxrightbordersum=0;
          rightbordersum=0;
          for(int i=center+1;i<=right;i++)   //找右半部分最大
          {
              rightbordersum+=a[i];
              if(rightbordersum>maxrightbordersum)
                maxrightbordersum=rightbordersum;
          }
          return max3(maxleftbordersum,maxrightbordersum,maxleftbordersum+maxrightbordersum);   //比较哪个更大返回大的
     }
     int Maxsubsequencesum(int a[],int n)   //简化一下到时候主函数里更好用
     {
         return Maxsubsum(a,0,n-1);
     }

    int main()
    {
        int k,sum=0;
        scanf("%d",&k);
        for(int i=0;i<k;i++)
            scanf("%d",&a[i]);
        sum=Maxsubsequencesum(a,k);
        printf("%d\n",sum);
        return 0;
    }

第二种
是一种联机算法 (用的时间少(O(N))) 书上解释是说它只对数据进行一次扫描,一旦完成对a[i]的读入和处理就不需要再记忆它了。

代码主要意思就是如果a[i]是负数就变为0,就舍弃,然后下一个数a[i+1]变为新的字串起始点,大于Max的就保存,循环。

看代码吧

            #include
            #include
            #include
            #include
            #include
            #include
            #include
            using namespace std;
            typedef long long ll;
            int a[100000];
    int Maxsubsequencesum(int a[],int n)
    {

        int thissum,maxsum;
        thissum=maxsum=0;
        for(int i=0;i<n;i++)
        {
            thissum+=a[i];
            if(thissum>maxsum)
                maxsum=thissum;
            else if(thissum<0)
                thissum=0;
        }
        return maxsum;
    }

    int main()
    {
        int k,sum=0;
        scanf("%d",&k);
        for(int i=0;i<k;i++)

            scanf("%d",&a[i]);
        sum=Maxsubsequencesum(a,k);
        printf("%d\n",sum);
        return 0;
    }

不理解可以再去看看其他的一些联机算法的例子,有些大佬写的超好的说

你可能感兴趣的:(算法)