[HDOJ 1003]动态规划法求和最大的连续子序列
Posted zhouyijoe
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[HDOJ 1003]动态规划法求和最大的连续子序列相关的知识,希望对你有一定的参考价值。
题目地址: http://acm.hdu.edu.cn/showproblem.php?pid=1003
1 #include <cstdio> 2 #include <climits> 3 #include <algorithm> 4 using namespace std; 5 6 int getMaxSum(int* arr,int high) 7 { 8 const int SIZE = 1e4; 9 int dp[SIZE] = {0}; 10 11 for(int i = 0;i <= high;++i) 12 { 13 if(i == 0) 14 { 15 dp[i] = arr[i]; 16 } 17 else if(i > 0) 18 { 19 dp[i] = max(dp[i - 1] + arr[i],arr[i]); 20 } 21 } 22 23 int maxSum = INT_MIN; 24 for(int i = 0;i <= high;++i) 25 { 26 if(dp[i] > maxSum) 27 { 28 maxSum = dp[i]; 29 } 30 } 31 32 return maxSum; 33 }
现在我们已经解决了如何求最大和的问题了,不过这道oj题还要求我们给出所求连续子序列的起点位置和终点位置。如果是用暴力枚举法的话,很容易就可以记录下起点位置和终点位置。不过如果是用动态规划法的话,想要记录下位置,可能还得稍微动一下脑子。不过也不难。我们自己手动模拟一下求dp值的过程,就可以找到其中的规律了。
下图是计算一个数组的所有dp值的过程。
仔细观察上图,就可以找到其中的规律:计算一个数组的所有dp值的过程,其实是一个"从头加到尾"的过程,只不过在dp值小于零的地方需要"断开"。具体地说,计算一个数组的所有dp值时,我们需要做的是从第一个元素开始,依次取数,把这些数一个一个加起来。如果当加到第i个数时所得到的和为一个负数,则把前面i个数的和直接"丢掉"不要,然后从第i + 1个数开始,再从零开始加,之后循环往复地重复上述步骤。于是,我们就可以以这样一种方法得到每个dp值对应的连续子序列的起点位置和终点位置:一开始把起点位置设为1,也就是整个数组的起点。显然,dp(1)的终点位置也是1。然后求后面的dp值时,只要dp(i - 1)的值是非负的,那么dp(i)对应的起点位置就是dp(i - 1)的起点位置,而终点位置就是dp(i - 1)的终点位置 + 1。如果dp(i - 1)的值是负数,则dp(i)对应的起点位置就应该是i,终点位置也是i。
也许会有人觉得需要另外开两个数组,保存每个dp值对应的起点位置和终点。但其实没必要。我们只需要在每求出一个dp值时,都去更新一下最大值和起点终点位置即可。具体的做法见下面的代码。
1 #include <cstdio> 2 #include <climits> 3 4 const int SIZE = 1e5 + 10; 5 int arr[SIZE]; 6 int dp[SIZE]; 7 8 int main() 9 { 10 int nCase = 0; 11 scanf("%d",&nCase); 12 13 for(int i = 1;i <= nCase;++i) 14 { 15 int n = 0; 16 scanf("%d",&n); 17 for(int j = 0;j < n;++j) 18 { 19 scanf("%d",&arr[j]); 20 } 21 22 int maxSum = INT_MIN; 23 int mLow = 0; //和最大的连续子序列的下界 24 int mHigh = 0; //和最大的连续子序列的上界 25 int tLow = 0; //以第j个元素为结尾的和最大的连续子序列的下界 26 int tHigh = 0; //以第j个元素为结尾的和最大的连续子序列的上界 27 for(int j = 0;j < n;++j) 28 { 29 if(j == 0) 30 { 31 dp[j] = arr[j]; 32 tLow = j; 33 tHigh = j; 34 } 35 else if(j > 0) 36 { 37 if(dp[j - 1] >= 0) 38 { 39 dp[j] = dp[j - 1] + arr[j]; 40 tHigh = j; 41 } 42 else if(dp[j - 1] < 0) 43 { 44 dp[j] = arr[j]; 45 tLow = j; 46 tHigh = j; 47 } 48 } 49 50 if(dp[j] > maxSum) 51 { 52 maxSum = dp[j]; 53 mLow = tLow; 54 mHigh = tHigh; 55 } 56 } 57 58 if(i == nCase) 59 { 60 printf("Case %d: %d %d %d ",i,maxSum,mLow + 1,mHigh + 1); 61 } 62 else 63 { 64 printf("Case %d: %d %d %d ",i,maxSum,mLow + 1,mHigh + 1); 65 } 66 } 67 }
上面的代码就已经足够让这道oj题AC了。不过,其实上面的做法还不是最好的做法。我们其实没有必要开一个数组保存dp值。就像上面说过的,求dp值的过程其实就是"从头加到尾"的过程。我们只需要弄一个变量sum,从数组的第一个元素开始,一个一个取数加到sum中,每加一个数后都用sum去更新一下最大值。一旦sum的值为负数,就把sum重新置零,也就是把前面得到的总和"丢掉"不要,然后再从下一个数开始一个一个取数加到sum中......不断地重复这样的步骤即可。
下面的代码就是一种比较好的做法。
#include <cstdio> #include <climits> int main() { int nCase = 0; scanf("%d",&nCase); for(int i = 1;i <= nCase;++i) { int n = 0; scanf("%d",&n); int high = 0; int low = 0; int lowTemp = 1; int maxSum = INT_MIN; int sum = 0; int ele = 0; for(int j = 1;j <= n;++j) { scanf("%d",&ele); sum += ele; if(sum > maxSum) { maxSum = sum; low = lowTemp; high = j; } if(sum < 0) { sum = 0; lowTemp = j + 1; } } if(i == nCase) { printf("Case %d: %d %d %d ",i,maxSum,low,high); } else { printf("Case %d: %d %d %d ",i,maxSum,low,high); } } }
(完)
以上是关于[HDOJ 1003]动态规划法求和最大的连续子序列的主要内容,如果未能解决你的问题,请参考以下文章