- 著者:wugenqiang
- 個人ブログ:https : //wugenqiang.gitee.io
- 研究ノート:https : //wugenqiang.gitee.io/cs-notes
最大サブシーケンス合計
【タイトル】
给定(可能有负数)整数a(1)、a(2)、……a(n),求 a(1)+a(2)+……+a(j)的最大值。
为方便起见,若所有的整数为负数,则最大子序列和为0.
也就是:在一系列整数中,找出连续的若干个整数,这若干个整数之和 最大。
【コード実装】
- 方法1:徹底的な方法
- ネストされた3レベルのforループ、実行時間O(N ^ 3)により、すべてが可能な限り
- アルゴリズムのアイデア:各サブシーケンスの合計を計算します。つまり、シーケンス内のi番目からj番目の数の合計を計算し(j> = i)、比較します。
- C言語バージョン:関数プロトタイプはint maxSubSum(int a []);です。
#include <stdio.h>
int maxSubSum(int a[]){
int maxSum = 0;
int sum, i, j, k;
int len = sizeof(a) / sizeof(int);
for(i = 0; i < len; i++){
for(j = i; j < len; j++){
sum = 0;
for(k = i; k <= j; k++){
sum += a[k];//计算 a[i] 到 a[j] 的和
}
if(sum > maxSum){
maxSum = sum;
}
}
}
return maxSum;
}
int main(){
int a[] = {-2, 11, -4, 13, -5, -2};
int max = maxSubSum(a);
printf("%d\n",max);
return 0;
}
- C言語バージョン:関数プロトタイプはint maxSubSum(int a []、int n);です。
#include <stdio.h>
int maxSubSum(int a[], int n){
int maxSum = 0;
int sum, i, j, k;
for(i = 0; i < n; i++){
for(j = i; j < n; j++){
sum = 0;
for(k = i; k <= j; k++){
sum += a[k];//计算 a[i] 到 a[j] 的和
}
if(sum > maxSum){
maxSum = sum;
}
}
}
return maxSum;
}
int main(){
//int a[] = {-2, 11, -4, 13, -5, -2};
int a[] = { -2, 4, -3, 5, 7, -1, 8, 1 };
int len = sizeof(a) / sizeof(int);//有负数所以 strlen(a) 不能用
int max = maxSubSum(a, len);
printf("%d\n",max);
return 0;
}
-
方法2:
- 最初のものに基づいて簡略化し、forループの1つの層を削除し、実行時間O(N ^ 2)
- アルゴリズムのアイデア:最初のアルゴリズムの3番目のforループには、iからjまでの合計の計算など、不要な反復計算がたくさんありますが、iからj-1までの合計は前のループで計算されているため、必要ありません。計算を繰り返して、forループを削除できるようにします
-
C言語バージョン
#include <stdio.h>
int maxSubSum(int a[], int n){
int maxSum = 0;
int sum, i, j;
for(i = 0; i < n; i++){
sum = 0;
for(j = i; j < n; j++){
sum += a[j];
if(sum > maxSum){
maxSum = sum;
}
}
}
return maxSum;
}
int main(){
//int a[] = {-2, 11, -4, 13, -5, -2};
int a[] = { -2, 4, -3, 5, 7, -1, 8, 1 };
int len = sizeof(a) / sizeof(int);//有负数所以 strlen(a) 不能用
int max = maxSubSum(a, len);
printf("%d\n",max);
return 0;
}
- 方法3:分割して征服する
- アルゴリズムのアイデア:問題を2つのほぼ等しいサブ問題に分割し、それらを再帰的に解決するこれは「分割された」部分です。「ガバナンス」の段階では、2つのサブ問題のソリューションにパッチが適用され、いくつかの追加作業が実行され、最終的に問題全体のソリューションが取得されます。
- この問題では、シーケンスを中央から2つの部分に分割すると、最大のサブシーケンスの合計が、入力データの左半分、右半分、または境界を越えた3か所に表示される可能性があります。最初の2つのケースは再帰的に解決できます。3番目のケースの最大合計は、前半の最大合計(前半の最後の要素を含む)と後半の最大合計(後半の最初の要素を含む)を見つけることで取得できます。結果は合計であり、この時点で2つの合計が加算されます。
- 実行時間O(NログN)
- C言語バージョン
#include <stdio.h>
int maxSubSum(int a[], int left, int right){
// 判断是否只有一个元素
if (left == right) {
if (a[left] > 0) {
return a[left];
} else {
return 0;
}
}
int center = (left + right) / 2;
int maxLeftSum = maxSubSum(a, left, center);
int maxRightSum = maxSubSum(a, center + 1, right);
// 左端处理
int maxLeftBorderSum = 0;
int leftBorderSum = 0;
int i;
for (i = center; i >= left; i--) {
leftBorderSum += a[i];
if (leftBorderSum > maxLeftBorderSum) {
maxLeftBorderSum = leftBorderSum;
}
}
// 右端处理
int maxRightBorderSum = 0;
int rightBorderSum = 0;
for (i = center + 1; i <= right; i++) {
rightBorderSum += a[i];
if (rightBorderSum > maxRightBorderSum) {
maxRightBorderSum = rightBorderSum;
}
}
// 返回最大值
int maxBorderSum = maxLeftBorderSum + maxRightBorderSum;
return maxBorderSum > maxLeftSum ? maxBorderSum > maxRightSum ? maxBorderSum : maxRightSum
: maxLeftSum > maxRightSum ? maxLeftSum : maxRightSum;
}
int main(){
//int a[] = {-2, 11, -4, 13, -5, -2};
int a[] = { -2, 4, -3, 5, 7, -1, 8, 1 };
int max = maxSubSum(a, 0, sizeof(a) / sizeof(int) - 1);
printf("%d\n",max);
return 0;
}
-
方法4:最適な開始点、スキャン方法
- アルゴリズムのアイデア:[i]が最大のシーケンスの開始点であるとすると、[i]が負の場合、最適なシーケンスの開始点を表すことができません。 [i + 1]を開始点として使用することで改善されます。
- 同様に、負のサブシーケンスを最適なサブシーケンスのプレフィックスにすることはできません。
- 実行時間:O(N)
-
C言語バージョン
#include <stdio.h>
int maxSubSum(int a[], int n){
int maxSum = 0;
int sum = a[0], i;
/*考虑如果全是负数,那么返回最大的负数,
如果最后的和为正,那么就使用扫描法*/
for(i = 1; i < n; i++){
if(sum < 0){//当前数小于0,换为下一个数
sum = a[i];
}else{
sum += a[i];
}
if(sum > maxSum){
maxSum = sum;
}
}
return maxSum;
}
int main(){
//int a[] = {-2, 11, -4, 13, -5, -2};
int a[] = { -2, 4, -3, 5, 7, -1, 8, 1 };
int len = sizeof(a) / sizeof(int);//有负数所以 strlen(a) 不能用
int max = maxSubSum(a, len);
printf("%d\n",max);
return 0;
}