算法第三章上机实验报告
实践题目:
最大子段和
给定n个整数(可能为负数)组成的序列a[1],a[2],a[3],…,a[n],求该序列如a[i]+a[i+1]+…+a[j]的子段和的最大值。当所给的整数均为负数时,定义子段和为0。
要求算法的时间复杂度为O(n)。
输入格式:
输入有两行:
第一行是n值(1<=n<=10000);
第二行是n个整数。
输出格式:
输出最大子段和。
输入样例:
6
-2 11 -4 13 -5 -2
输出样例:
20
- 问题描述:在给定的数段中找出和最大的子段,由于数段中存在负数,且负数的位置不确定;并且前一次的最大子段和有可能是负数,所以要做出比较来决定是否将其算进来。
- 算法描述:首先将给定的数段用一个一维数组存起来,方便后续的操作。并创建一个辅助数组b[ ],用于保存每次获取子数段的和,最后比较b[ ]数组里所有元素的大小,最大的即为要求的最大子段和。
int max_son_sum = b[0];
for(j = 1; j <= n; j++){
if(max_son_sum < b[j])
max_son_sum = b[j];
}
由于解决此问题会从第一个数开始检索,后一个问题的解都是依赖于前面的子问题,故分析可得b[j]的动态规划递归式为:
b[j] = max{b[j-1] + a[j], a[j]} (1 <= j <= n)
即表示,当b[j]的子问题b[j-1]所得的和为负数时,放弃b[j-1]作为b[j]和的一部分,只取j下标当前的数,即a[j];若b[j-1]所得的和为正数,把b[j-1]作为b[j]和的一部分,并加上j下标当前的数,即b[j-1] + a[j]。并操作完整个a[ ]数组,得到最后的b[ ]数组。
b[0] = a[0];
int i, j;
for(i = 1; i < n; i++){
b[i] = (b[i-1] > 0) ? (b[i-1] + a[i]) : a[i];
}
这里需要注意的是,第一个子段只有一个数,所以它的最大子段和为其本身;需要将a[0]作为初值赋给b[0]。
b[0] = a[0];
具体算法如下:
int b[10005];
int MaxSonSum(int a[10005], int n){
b[0] = a[0];
int i, j;
for(i = 1; i < n; i++){
b[i] = (b[i-1] > 0) ? (b[i-1] + a[i]) : a[i];
}
int max_son_sum = b[0];
for(j = 1; j <= n; j++){
if(max_son_sum < b[j])
max_son_sum = b[j];
}
return max_son_sum;
}
-
- 算法时间及空间复杂度分析:此算法由于只有一个循环语句来对原问题和子问题进行操作,故时间复杂度为O(n);而开了一个辅助数组用于存放结果且在输出最后的结果时进行了比较,故空间复杂度为O(n)。
- 心得体会:分针对此类的动态规划题目,最重要的是要解出其动态规划递归式。在做第二道题时其实就是将上一道题的核心递推式改一下,主体框架还是差不多的。再者就是,循环语句的范围,即填表的范围,这也得好好看一下,填表顺序和方法如是。