题意:求一个数列的最大连续和,并输出起止端。
题目链接: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; }
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(); } }