
注解
此题与hdu1003是类似的。只是输入输出的格式略有不同。
核心仍然是动态规划法求最大子列和。
下面把hdu1003的博客复制过来稍加改动:
1、用动态规划方法解决最大子列和问题。关键是设置一个dp[]数组,dp[i]表示以i结尾的最大子列和。
如dp[0]表示以0作为结尾的最大子列和,dp[1]表示以1作为结尾的最大子列和。
因此,要求出整个序列的最大子列和,只需要从头到尾遍历dp,找出最大值。这是最原始的最大子列和问题的解法。
2、本题除了要求输出最大和,还要求输出起始位置元素值和终止位置元素值。这就要在dp[]数组的基础上,再增加start[]数组,start[i]表示以i作为结束的最大和的起始位置元素值。
3、动态规划的核心:找出状态转移方程:dp[i] = max(a[i], dp[i-1]+a[i])。
本质就是,如果dp[i-1]>=0,dp[i]就等于dp[i-1]+a[i]。如果dp[i-1]<0,就输出a[i]。
4、一个小的注意点:初始化。题目不保证所有数字都是非负数,因此初始化答案时,应该是用第一个数字初始化。而不能简单用一个负数。因为可能出现所有数字都比这个负数小的情况,这样这个负数就会被误当成最终答案而输出。
5、此题额外增加了一种情况,当输入全部为负数时,要输出0,所以代码中需要增加一个是否全为负数的判断。技巧是用flag。
代码
#include <iostream>
#include <string.h>
using namespace std;
const int MAX = 10001;
int a[MAX];
int dp[MAX];
int start[MAX];
void process(int len) {
dp[0] = a[0];
start[0] = 0;
for(int i=1; i<len; i++) {
if(a[i]>dp[i-1]+a[i]) {
dp[i] = a[i];
start[i] = a[i];
} else {
dp[i] = dp[i-1]+a[i];
start[i] = start[i-1];
}
}
}
int main() {
int n;
scanf("%d", &n);
while(n) {
memset(a, 0, sizeof(a));
memset(dp, 0, sizeof(dp));
memset(start, 0, sizeof(start));
int isallNegative = 1;
for(int j=0; j<n; j++) {
scanf("%d", &a[j]);
if(a[j]>=0) {
isallNegative = 0;
}
}
if(isallNegative) {
printf("%d %d %d\n", 0, a[0], a[n-1]);
} else {
process(n);
int max = dp[0];
int ansStart = a[0];
int ansEnd = a[0];
for(int j=1; j<n; j++) {
if(dp[j]>max) {
max = dp[j];
ansStart = start[j];
ansEnd = a[j];
}
}
printf("%d %d %d\n", max, ansStart, ansEnd);
}
scanf("%d", &n);
}
return 0;
}
结果

本文详细解析了最大子列和问题的动态规划解法,通过设置dp[]数组记录以i结尾的最大子列和,结合start[]数组追踪起始位置元素值。文章介绍了状态转移方程,特别注意了初始化及全负数输入的处理。
588

被折叠的 条评论
为什么被折叠?



