4つのソリューション-サブシーケンスの最大連続サブシーケンス合計(通常のソリューション、合計ソリューション、分割統治、O(n)レベルのソリューション)を検索(インタビューの古典的な質問)

効率的な表現のための刺激的な使用の少ないコード


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;
} 

この記事が役に立った場合は、ブロガーにいいね!みんなのいいねが私の作品の最大のモチベーションです!

おすすめ

転載: blog.csdn.net/weixin_43899069/article/details/108178119