効率的な表現のための刺激的な使用の少ないコード
4つの解決策のうち、最初の解決策は、法則と知識を学び、基礎として機能する一般的な方法です。2番目の解決策は、最初の解決策に基づいて考えを改善します。3番目の解決策は、有名な分割統治法です。再帰の知識は、「効率的なアルゴリズム設計」の基礎と見なすことができます。解決策4は、O(n)の複雑さ、1つの単語、魔法で最大の連続サブシーケンスの合計を解決します。4つのソリューションは段階的で、効率は徐々に向上しますが、これは非常に微妙です。初心者は、それぞれをマスターすることをお勧めします。
ソリューション1:最も一般的なソリューションは、0からn-1までの配列の値を表すiループを定義し、a [i]から開始してa [j]で終了することを意味するjループを定義することです。kループを定義し、a [i]からa [j]までのすべての数値を追加して、最後に最大のサブシーケンス合計を見つけます。
時間の複雑さ:O(n ^ 3)
#include<bits/stdc++.h>
using namespace std;
int main() {
int n; cin>>n; int a[n];
for(int i = 0; i < n; i++) cin>>a[i];
int sum = a[0]; //不能等于0,因为可能是全负序列
int temp = 0;
for(int i = 0; i < n; i++)
for(int j = i; j < n; j++) //二重循环, 从i截止到j遍历
for(int k = i; k <=j; k++) //i-j序列的区间和。
temp += a[k];
sum = max(sum, temp);
}
cout << sum << endl;
return 0;
}
ソリューション2:Sで定義された列の数[n-] 、S[i]=a[0]+a[1]+...+a[i]
. もしJ> I、その後S[j]-S[i-1]=a[j]+a[j-1]+...+a[i]
。つまり、2つのforループ(1つはiと1つはjのループ)をネストすることで、任意のサブシーケンスの合計を表現し、最後にそれらを比較できます。
時間の複雑さ:O(n ^ 2)
#include<bits/stdc++.h>
using namespace std;
int main() {
int n; cin>>n; int a[n];
for(int i = 0; i < n; i++) cin>>a[i];
int S[n+1]; S[0]=0;
for(int i = 1; i <= n; i++) S[i] = a[i] + S[i-1];
int best = 0;
for(int i = 1; i <= n; i++)
for(int j = i; j <= n; j++) best = max(best, S[j]-S[i-1]);
return 0;}
解決策3:名前が示すように、分割して征服する。
分割統治アルゴリズムは、通常、次の3つのステップに分割されます
。1.問題を分割する:問題のインスタンスをサブ問題に分割します。
2.再帰的解決策:サブ問題を再帰的に解決します。
3.マージ問題:サブ問題のソリューションをマージして、元の問題のソリューションを取得します。
最大の連続合計の分割統治アルゴリズム:
1.シーケンスを可能な限り同じ要素数の2つの半分に分割します
。2 。完全に左半分または完全に右にある最適なシーケンスを見つけます。3 。左半分
の開始点と左半分の終了点を見つけます。右半分の最大連続合計シーケンスは、副問題の最適解と比較されます。
再帰を含むコードはより複雑ですが、本当に理解できない場合は覚えてください。子供の頃に覚えた古代の詩について考えてください。暗記して暗記しても、時間の経過とともに徐々にその意味を理解するようになります。
時間の複雑さ:O(nlogn)(このレベルの時間の複雑さは、通常、ほとんどの競争を処理できます)
#include<bits/stdc++.h>
using namespace std;
int a[10005];
int longsub(int *a, int l, int r) {
//返回数组在左闭右开区间[x,y)中最大连续和
//为什么不返回a[r]呢? 因为该区间是左闭右开区间
if(r-l == 1) return a[l]; //只有一个元素直接返回
int mid = l + (r-l)/2; //分治第一步:划分成[l,m)和[m,r)
int maxs = max(longsub(a,l,mid), longsub(a,mid,r)); //分治第二步:递归求解
int v, Lmax, Rmax;
v=0; Lmax = a[mid-1]; //分治第三步:合并(1)——从分界点开始往左的最大连续和l
for(int i=mid-1; i>=l; i--) Lmax=max(Lmax, v+=a[i]);
v=0; Rmax=a[mid]; //分治第三步:合并(2)——从分界点开始往右的最大连续和r
for(int i=mid; i<r; i++) Rmax=max(Rmax, v+=a[i]);
return max(maxs, Lmax+Rmax);
}
int main() {
int n; cin>>n;
for(int i = 0; i < n; i++) cin>>a[i];
cout << longsub(a, 0, n);
return 0;}
ソリューション4:
非常に特別なソリューション、コアアイデア:i番目の要素に移動するときに、その前の連続するサブシーケンスの合計が0より大きいかどうかを判断します。それが0より大きい場合、位置iで終わる最大の連続するサブシーケンスの合計は要素ですiと正面玄関の連続したサブシーケンスの合計が追加されます。それ以外の場合は、位置iで終わる最大の連続したサブシーケンスの合計が要素iになります。
時間の複雑さ:O(n)
#include<iostream>
using namespace std;
int main() {
int n; cin>>n; int a[n];
for(int i = 0; i < n; i++) cin>>a[i];
int maxsum, maxhere;
maxsum = maxhere = a[0]; //初始化最大和为a[0]
for(int i = 1; i < n; i++) {
if(maxhere <= 0) maxhere = a[i]; //若前面位置连续子序列和小于0,则以当前位置开始
else maxhere += a[i]; //若大于零,则加入
if(maxhere > maxsum) maxsum = maxhere; //更新
}
cout << maxsum << endl;
return 0;
}
この記事が役に立った場合は、ブロガーにいいね!みんなのいいねが私の作品の最大のモチベーションです!