hdu - 1003 - Max Sum

来源:互联网 发布:美英特殊关系知乎 编辑:程序博客网 时间:2024/05/06 02:19

题意:求一个数列的最大连续和,并输出起止端。

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

——>>做这题三次了,似乎每次的写法都不一样,现在觉得,至少有两种想法可解决此题:

一、以数列的每个数为末端的最大连续和从左到右遍历一次。

二、分治与递归,[L, R)的最大连续和,取其中点M = L + (R-L) / 2,最大连续和要么在[L, M),要么在[M, R),要么横穿M。

两种想法都是dp……

直接dp:

#include <cstdio>using namespace std;const int maxn = 100000 + 10;int a[maxn];int main(){    int T, N, i, cnt = 1;    scanf("%d", &T);    while(T--)    {        scanf("%d", &N);        for(i = 0; i < N; i++) scanf("%d", &a[i]);        int max_sum = a[0], max_L = 0, max_R = 0, cur_sum = a[0], L = 0, R = 0;        for(i = 1; i < N; i++)        {            if(cur_sum < 0)            {                cur_sum = a[i];                L = R = i;            }            else            {                cur_sum += a[i];                R = i;            }            if(cur_sum > max_sum)            {                max_sum = cur_sum;                max_L = L;                max_R = R;            }        }        printf("Case %d:\n", cnt++);        printf("%d %d %d\n", max_sum, max_L+1, max_R+1);        if(T) printf("\n");    }    return 0;}

分治与递归dp:

#include <iostream>using namespace std;const int maxn = 100000 + 10;       //1<=N<=100000int A[maxn];       //A为输入数组int dp(int L, int R, int &pre, int &suf)        //找数组A中[L, R)上的最大连续和,其中最大连续和的左边界放入pre中,右边界放入suf中{    if(R - L == 1)      //如果只有一个元素,直接返回    {        pre = L;        suf = L;        return A[L];    }    int M = L + (R - L)/2, i, l_pre, l_suf, r_pre, r_suf;       //取中值M进行分治    int max_left = dp(L, M, l_pre, l_suf);      //求左串的最大连续和    int max_right = dp(M, R, r_pre, r_suf);     //示右串的最大连续和    int max_pre = -214748364, max_suf = -214748364, sum = 0;        //max_suf为左串的最大后缀和,max_pre为右串的最大前缀和    for(i = M-1; i >= L; i--)       //求max_suf    {        sum += A[i];        if(sum >= max_suf)      //更新        {            pre = i;        //预先设[L, R)上的最大连续和横穿M,将其最大连续和的左边界更新            max_suf = sum;        }    }    sum = 0;    for(i = M; i < R; i++)      //求max_pre    {        sum += A[i];        if(sum > max_pre)       //更新        {            suf = i;        //预先设[L, R)上的最大连续和横穿M,将其最大连续和的右边界更新            max_pre = sum;        }    }    sum = max_suf + max_pre;        //横穿M的最大连续和    int MAX;        //要返回的最大连续和    if(max_left >= sum)     //当左子串的最大连续和大于等于横穿M的最大连续和时(注意不用>,因为相等的时候取先出现的)    {        pre = l_pre;        //更新最大连续和的左边界        suf = l_suf;        //更新最大连续和的右边界        MAX = max_left;     //更新最大连续和    }    else    {        MAX = sum;      //更新最大连续和    }    if(max_right > MAX)     //当右子串的最大连续和大于横穿M的最大连续和时(注意不用>=,因为相等的时候取先出现的)    {        pre = r_pre;        //更新最大连续和的左边界        suf = r_suf;        //更新最大连续和的右边界        MAX = max_right;        //更新最大连续和    }    return MAX;     //返回最大连续和}int main(){    int T, N, i, cnt = 1, l, r;    cin>>T;    while(T--)    {        cin>>N;        for(i = 1; i <= N; i++)            cin>>A[i];        cout<<"Case "<<cnt++<<":"<<endl;        int max_sum = dp(1, N+1, l, r);        cout<<max_sum<<" "<<l<<" "<<r<<endl;        if(T) cout<<endl;    }    return 0;}

直接dp的Java版:

import java.util.Scanner;public class Main {final static int maxn = 100000 + 10;static int a[] = new int[maxn];public static void main(String[] args) {int T, N, i, cnt = 1;Scanner cin = new Scanner(System.in);T = cin.nextInt();while(T-->0){N = cin.nextInt();for(i = 0; i < N; i++) a[i] = cin.nextInt();int max_sum = a[0], max_L = 0, max_R = 0;int cur_sum = a[0], L = 0, R = 0;for(i = 1; i < N; i++){if(cur_sum < 0){cur_sum = a[i];L = R = i;}else{cur_sum += a[i];R = i;}if(cur_sum > max_sum){max_sum = cur_sum;max_L = L;max_R = R;}}System.out.println("Case "+(cnt++)+":");System.out.println(max_sum+" "+(max_L+1)+" "+(max_R+1));if(T>0) System.out.println();}cin.close();}}


原创粉丝点击