日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

最大子段和 分治与动态规划

發布時間:2024/1/17 编程问答 26 豆豆
生活随笔 收集整理的這篇文章主要介紹了 最大子段和 分治与动态规划 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

問題:
? 給定n個整數(可能為負數)組成的序列a[1],a[2],a[3],…,a[n],求該序列如a[i]+a[i+1]+…+a[j]的子段和的最大值。當所給的整均為負數時定義子段和為0,依此定義,所求的最優值為:
????Max{0,a[i]+a[i+1]+…+a[j]},1<=i<=j<=n
??? 例如,當(a1,a2,a3,a4,a4,a6)=(-2,11,-4,13,-5,-2)時,最大子段和為20。

問題求解:

方法一:枚舉

學過程序設計的都會,那就是枚舉i和j,求i和a[i]到a[j]之間的和的最大值。

時間復雜度O(n^3)。這顯然是不能接受滴。其實這其中進行了大量的重復計算。

#include<iostream> using namespace std;/*簡單算法: **v[0]不保存數據 **T(n)=O(n^2). */ int MaxSum(int *v,int n,int *besti,int *bestj) {int sum=0;int i,j;for (i=1;i<=n;i++){int thissum=0;for (j=i;j<=n;j++){thissum+=v[j];if (thissum>sum){sum=thissum;*besti=i;*bestj=j;}}}return sum; }int main(void) {int n,m,i,j,k=0;int arr[100];cin>>n;m = n;while(n--){cin>>arr[k++];}int r = MaxSum(arr,m,&i,&j);cout<<i<<" "<<j<<endl;cout<<r<<endl;return 0; }

方法二:分治

考慮能不能有O(n*logn)的算法呢?當然有了……

如果將給定的序列a[1..n]分成長度相等的兩段a[1..n/2]和a[n/2+1:n],分別求出這兩段的最大字段和。則該給定序列的最大字段和有三種情行:

1)和a[1..n/2]的最大字段和相同。

2)和a[n/2+1:n]的最大字段和相同。

3)最大字段和包含兩部分,一部分在中,另一部分在a[n/2+1..n]中。

前兩種情形我們可以用遞歸方法求出,第三種情形可以分別求出兩部分的最大字段和值再相加(注:a[1..n/2]這部分求最大字段和要以a[n/2]結束,a[n/2+1..n] 這部分求最大字段和要以a[n/2+1]開始)。序列的最大字段和即為這三種情形的最大值。

這種情況下,顯然時間復雜度為O(n*logn)。要是有O(n)的算法該多好呢?事實上還真有。這自然就是要想到動態規劃了吧!!!

?

#include<iostream> using namespace std;/*分治法: **將a[1n]分成a[1n/2]和a[n/2+1n],則a[1n]的最大字段和有三種情況: **(1)a[1n]的最大子段和與a[1n/2]的最大子段和相同 **(2)a[1n]的最大子段和與a[n/2n]的最大子段和相同 **(3)a[1n]的最大子段和為ai++aj,1<=i<=n/2,n/2+1<=j<=n **T(n)=2T(n/2)+O(n) **T(n)=O(nlogn) */ int MaxSum_DIV(int *v,int l,int r) { int k,sum=0; if(l==r) return v[l]>=0?v[l]:0; else { int center=(l+r)/2; int lsum=MaxSum_DIV(v,l,center); int rsum=MaxSum_DIV(v,center+1,r); int s1=0; int lefts=0; for (k=center;k>=l;k--) { lefts+=v[k]; if(lefts>s1) s1=lefts; } int s2=0; int rights=0; for (k=center+1;k<=r;k++) { rights+=v[k]; if(rights>s2) s2=rights; } sum=s1+s2; if(sum<lsum) sum=lsum; if(sum<rsum) sum=rsum; } return sum; } int main(void) { int n,m,i,j,k=0; int arr[100]; cin>>n; m = n;//記錄數組的長度 while(n--) { cin>>arr[k++]; } int r = MaxSum_DIV(arr,0,m); cout<<r<<endl; return 0; }

方法三:動態規劃

動態規劃不太好理解

#include<iostream> using namespace std;int MaxSum_DYN(int *a,int n) { int temp = 0,maxn = 0,k=1,i; int start,end; for(i=1;i<=n;i++) { temp+=a[i]; if(temp>maxn) { maxn = temp; start = k; end = i; } if(temp<0) { temp = 0; k = i+1; } } return maxn; } int main(void) { int n,m,i,j,k=0; int arr[100]; cin>>n; m = n;//記錄數組的長度 while(n--) { cin>>arr[k++]; } int r = MaxSum_DYN(arr,m); cout<<r<<endl; return 0; }

?分析一下這個算法,借用了一個臨時變量temp,其實有三種情況:

1. 若temp>maxn則更新maxn,并保存開始和結束位置;

2. 若temp<0則令temp = 0,因為temp<0則不可能繼續用temp更新最大值了;

3. 若0<temp<maxn,則不作操作,這是temp被認為是有潛力的,可能會用來更新后面的值。這樣的一次遍歷搜索到了所有的最大值。

(temp的使用時關鍵,好好理解這種思想。理解不了也沒關系,這是比較難想的方法。)

?編程珠璣上的經典題目,也已經被做爛了,除了最后一個方法,其他的都是浮云,但是最后一個方法寫得也比較啰嗦,k完全沒必要。

int Sum(int* a, int n) {int curSum = 0;int maxSum = 0;for (int i = 0; i < n; i++){if (curSum + a[i] < 0)curSum = 0;elsemaxSum = max(maxSum, curSum += a[i]);}return maxSum; }

?

轉載于:https://www.cnblogs.com/samjustin/p/4562109.html

總結

以上是生活随笔為你收集整理的最大子段和 分治与动态规划的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。