HDU 1003 Max Sum 分治法

思路: 将原数组递归分解为两个子数组,直到能求解出子解为止,再将答案合并。


难点:产生数组的最大子序列只有以下三种情况:

1.最大子序列为左子数组的最大子序列;

2.最大子序列为右子数组的最大子序列;

3.最大子序列为跨中点的最大子序列;(特殊情况)

依照以上三种情况分而治之即得解。


附ac代码

#include <stdio.h>
int a[100001];
 
void subcross(int arr[], int *low, int *mid, int *high, int *sum){
    int s = 0, maxl = -1000 - 1, maxr = -1000 - 1, l, r;
    for(int i = *mid; i >= *low; --i){ //以中点为基准,向左推进求最大左边界子序列
        s += arr[i];
        if(s >= maxl){
            l = i; maxl = s;
        }
    }  
    for(int i = *mid + 1, s = 0; i <= *high; ++i){ //以中点为基准,向右推进求最大右边界子序列
        s += arr[i];
        if(s > maxr){
            r = i; maxr = s;
        }
    }
    *sum = maxl + maxr; //合并,传递参数
    *low = l;
    *high = r;
}
 
void subarr(int arr[], int *low, int *high, int *sum){
    if(*low == *high){  //base case
        *sum = arr[*low];
        return;
    }
    int l = *low, r = *high, s1, s2, s;
    int m = (l + r) / 2;
    int l1 = l, l2 = m + 1, r1 = m, r2 = r;
    subarr(arr, &l1, &r1, &s1); //求左数组最大子数组
    subarr(arr, &l2, &r2, &s2); //求右数组最大子数组
    subcross(arr, &l, &m, &r, &s); //求跨中点最大子数组
    //合并,先左再中后右,因为题目要求求第一个最大子数组,也就是最左边的一个
    if(s1 >= s2 && s1 >= s){
        *low = l1; *high = r1; *sum = s1;
    }
    else if(s >= s1 && s >= s2){
        *low = l; *high = r; *sum = s;
    }
    else{
        *low = l2; *high = r2; *sum = s2;
    }  
}
 
int main(){
    int t, n, low, sum, high, i, j = 1;
    scanf("%d", &t);  //t表示测试的组数
    while(t-- && scanf("%d", &n)){  //n表示每组测试的元素个数
        i = 1; //用来标记下标,从1开始
        while(n--)
            scanf("%d", &a[i++]);
        low = 1;  //最左下标
        high = i - 1;  //最右下标
        subarr(a, &low, &high, &sum);
        printf("Case %d:\n%d %d %d\n", j++, sum, low, high);
        if(t) putchar('\n');
    }
    return 0;
}


posted on 2014-02-10 17:12  长木Qiu  阅读(167)  评论(0编辑  收藏  举报