hdu 1003 Max Sum(分而治之||最大连续子序列 dp)

题目:http://acm.hdu.edu.cn/showproblem.php?pid=1003

大意:给出无序的一串数字,求出它最大的子序和及其起点和终点,如果有多个答案输出第一个(左边)。for(i=1;i<=n;i++) sum[i]=(1+2+……+i) 然后再用sum[i]表示出各个子序和固然可行,但是10^5*10^5=1e10明显会超时。用分而治之的方法可以提高效率。

#include <iostream>
#include<cstdio>
using namespace std;
const int maxn=1e5+5;
int number[maxn];
void midcross(int &l,int &r,int &sum3){
    int mid=(l+r)/2,i;
    int lsum=-1005,rsum=-1005,l1,r1,ls=0,rs=0;
    for(i=mid;i>=l;i--){
        ls+=number[i];
        if(lsum<=ls){
            lsum=ls;
            l1=i;
        }
    }
    for(i=mid+1;i<=r;i++){
        rs+=number[i];
        if(rsum<=rs){
            rsum=rs;
            r1=i;
        }
    }
    sum3=lsum+rsum;
    l=l1;
    r=r1;
}
void subsum(int &l,int &r,int &sum){
    if(l==r){  sum=number[l]; return ;  }
    int mid=(l+r)/2,sum1,sum2,sum3;
    int l1=l,r1=mid,l2=mid+1,r2=r,l3=l,r3=r;
    subsum(l1,r1,sum1);
    midcross(l3,r3,sum3);
    subsum(l2,r2,sum2);
    if(sum1>=sum2&&sum1>=sum3){
        sum=sum1;
        l=l1;
        r=r1;
    }
    else if(sum3>=sum2&&sum3>sum1){
        sum=sum3;
        l=l3;
        r=r3;
    }
    else {
        sum=sum2;
        l=l2;
        r=r2;
    }
}
int main()
{
    //freopen("cin.txt","r",stdin);
    int t,i,j;
    cin>>t;
    for(i=1;i<=t;i++){
            int n,l,r,sum;
            scanf("%d",&n);
            for(j=1;j<=n;j++) scanf("%d",&number[j]);
            l=1;   r=n;
            subsum(l,r,sum);
            printf("Case %d:\n%d %d %d\n",i,sum,l,r);
            if(i<t)printf("\n");
    }
    return 0;
}
上面的分治递归大部分的求和计算都在midcross里完成,在subsum里主要是比较和参数传递。用到了引用,所以在函数中要用新的变量来保护l,r,sum不被函数里的函数改变。
如果用动态规划来做同样能解决这个问题:
#include <iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int maxn=1e5+5;
int number[maxn],dp[maxn];
int main()
{
    //freopen("cin.txt","r",stdin);
    int t,i,j;
    cin>>t;
    for(i=1;i<=t;i++){
        memset(dp,0,sizeof(dp));
        int n;
        scanf("%d",&n);
        for(j=1;j<=n;j++){
            scanf("%d",&number[j]);
        }
        int Max=-0x3f3f3f3f,tp=1,start=1,finish=1;
        for(j=1;j<=n;j++){
            dp[j]=dp[j-1]+number[j];
            if(dp[j]<number[j]){
                dp[j]=number[j];
                tp=j;
                //start=j;这里必须有一个保存起点的变量tp.  1 3 5(tp=1,finish=3) -3 -5 -2 -3(tp=7,finish=7)  但是Max应该取第一种情况。
            }
            if(Max<dp[j]){
                Max=dp[j];
                start=tp;
                finish=j;
            }
        }
        printf("Case %d:\n",i);
        printf("%d %d %d\n",Max,start,finish);
        if(i<t)putchar('\n');
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值