问题-给定一个n个数组,我们的任务是计算最大子数组和,即数组中连续值序列的最大可能和。当数组中可能有负值时,问题就很有趣了。数组={-1,2,4,-3,5,2,-5,2}。
第一种算法-
int best = 0;
for (int a = 0; a < n; a++) {
for (int b = a; b < n; b++) {
int sum = 0;
for (int k = a; k <= b; k++) {
sum += array[k];
}
best = max(best,sum);
}
}
cout << best << "\n";
第二种算法-
int best = 0;
for (int a = 0; a < n; a++) {
int sum = 0;
for (int b = a; b < n; b++) {
sum += array[b];
best = max(best,sum);
}
}
cout << best << "\n";
这是它在书中说的--从算法1中去掉一个循环,就很容易让算法1变得更高效。这可以通过在子数组右端移动时同时计算和来实现。
第二种算法中子数组的右端是如何移动的,有人能给我解释一下吗?
第一个版本对从indexa
到indexb
的子数组的所有子数组和进行强力比较,我们将这些子数组称为sumssubsum(a,b)
。
第二个版本也是蛮力的,但使用了subsum(a,b+1)==subsum(a,b)+array[b+1]
。
换句话说:要知道第一个10
元素的和,可以使用前面计算的第一个9
元素的和。如果你想用笔和纸解决问题,这将是显而易见的,但第一个版本没有做到这一点。相反,第一个版本对于a
和B
的所有组合都有两个嵌套循环,并且总是以新的sum=0
开头,这是相当浪费的。
只考虑第一个版本的外部循环:
for (int a = 0; a < n; a++) {
for (int b = a; b < n; b++) {
int sum = 0;
// ...
}
}
这里//...
计算子和(a,b)
。
在第二个版本中:
int best = 0;
for (int a = 0; a < n; a++) {
int sum = 0;
for (int b = a; b < n; b++) {
sum += array[b];
best = max(best,sum);
}
}
外循环负责在不同的“左端”启动子数组。内圈“移动”了“端点”。最内部的循环体计算subsum(a,b)
并且内部循环的下一次迭代使用上面的关系“移动”b
到下一个索引来计算subsum(a,b)==subsum(a,b-1)+array[b]
。因为A
是固定在内循环中的,所以作者说的是“移动右端”。