hdu - 1003 - Max Sum

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

题目链接: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<=100000
int 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();
	}
}


你可能感兴趣的:(hdu - 1003 - Max Sum)