• 子串和


    //wa,原因:超时,穷举法时间复杂度太大至少O(n^2),需要换一种高效的方法
    #include<stdio.h>
    int s[1000001];
    int main()
    {
        int i,j,t,m,n,maxSum;
        scanf("%d",&m);
        while(m--)
        {
            scanf("%d",&n);
            for(i=1;i<=n;++i)
            {
                scanf("%d",&s[i]);
                s[i]+=s[i-1];
            }    
            for(maxSum=-101,i=1;i<=n;++i)
            for(j=i;j<=n;++j)
            {
                t=s[j]-s[i-1];
                if(maxSum<t)
                    maxSum=t;
            }
            printf("%d ",maxSum);
        }
        return 0;
    }
    //AC
    #include<stdio.h>
    int main()
    {
        int t,m,n,sum,maxSum;
        scanf("%d",&m);
        while(m--)
        {
            scanf("%d%d",&n,&sum);
            maxSum=sum;
            while(--n)
            {
                scanf("%d",&t);
                if(sum<0) sum=t;
                else sum+=t;
                if(maxSum<sum) maxSum=sum;
            }   
            printf("%d ",maxSum);
        }
        return 0;
    }        
    利用Kadane算法,能够以时间复杂度为线性的情况下找到,以下是证明原理:

    说明Kadane算法的正确性,需要两个结论。首先,对于array[1...n],如果array[i...j]就是满足和最大的子串,那么对于任何 k(i<=k<=j),我们有array[i...k]的和大于0。因为如果存在k使得array[i...k]的和小于0,那么我们就有array[k+1...j]的和大于array[i...j],这与我们假设的array[i...j]就是array中和最大子串矛盾。

     次,我们可以将数组从左到右分割为若干子串,使得除了最后一个子串之外,其余子串的各元素之和小于0,且对于所有子串array[i...j]和任意 k(i<=k<j),有array[i...k]的和大于0。此时我们要说明的是,满足条件的和最大子串,只能是上述某个子串的前缀,而不可 能跨越多个子串。我们假设array[p...q],是array的和最大子串,且array[p...q],跨越了array[i...j],array[j+1...k]。根据我们的分组方式,存在i<=m<j使得array[i...m]的和是array[i...j]中的最大值,存在j+1<=n<k使得array[j+1...n]的和是array[j+1...k]的最大值。由于array[m+1...j]使得array[i...j]的和小于0。此时我们可以比较array[i...m]和array[j+1...n], 如果array[i...m]的和大于array[j+1...n]则array[i...m]>array[p...q],否 array[j+1...n]>array[p...q],无论谁大,我们都可以找到比array[p...q]和更大的子串,这与我们的假设矛 盾,所以满足条件的array[p...q]不可能跨越两个子串。对于跨越更多子串的情况,由于各子串的和均为负值,所以同样可以证明存在和更大的非跨越 子串的存在。对于单元素和最大的特例,该结论也适用。

    根据上述结论,我们就得到了Kadane算法的执行流程,从头到尾遍历目标数组,将数组分割为满足上述条件的子串,同时得到各子串的最大前缀和,然后比较各子串的最大前缀和,得到最终答案。我们以array={2, 1, 3, 4, 1, 2, 1, 5, 4}为例,来简单说明一下算法步骤。通过遍历,可以将数组分割为如下3个子串(-2),(1,-3),(4,-1,2,1,-5,4),这里对于(-2)这样的情况,单独分为一组。各子串的最大前缀和为-2,1,6,所以目标串的最大子串和为6。

     

     

    该问题是1977年Ulf Grenander提出的一个数字图像方面的问题,1984年Jay Kadane才给出了这个优美的解决方案。有些问题,看似解法简单,但是实际上其原理,要比代码复杂得多。

    这里我们需要注意,对于本题中的全是负数由于不满足上述的两条结论的情况,所以此算法就不行了,需要另外加以考虑!

  • 相关阅读:
    如何在SpringMVC中使用REST风格的url
    c#实现的udt
    数据库查询服务化-缓存和分页
    c#常用数据库封装再次升级
    c#数据库连接池Hikari重构升级
    c# 常用数据库封装
    聊聊数据存储查询
    c#分析SQL语句
    c# 分析SQL语句中的表操作
    c#最近博文结尾
  • 原文地址:https://www.cnblogs.com/yiranlaobaitu/p/3916883.html
Copyright © 2020-2023  润新知