今天學習了兩個基礎dp,分享一下。
求一段序列中的一段子串行,使得這段子序列的和最大:
①普通做法:
我們完全可以列舉法來做 ,即二層for迴圈,第一層列舉起點,二層列舉長度,用乙個sum來記錄當前的和。列舉完之後,最大子段和即為sum。
時間複雜度 o(n^2)
②dp做法:
1):我們用乙個陣列dp[i]記錄一下,以i結尾序列中子序列可產生的最大和。eg: 前三個數會產生 ,,,還。
2):這裡注意:以i結尾說明 i 一定為該序列的結尾。
3):所以我們對應第i個數來說,有以下兩種選擇:
第一種情況:第i個數與i-1個數構成乙個子段,即與以(i-1)結尾的子段融合。
第二種情況:第i個數單獨 成為 乙個子段 。
所以狀態轉移方程:dp[i]=max(dp[i-1]+num[i],num[i]);
4):接下來我們只需要記錄過程中產生的最大值就好了。
分析一下dp的最優性:以序列s=為例:(其實就等於畫**,因為一維就可以直接列了)
按照dp的原理:1.第乙個數可以自己乙個子段或者與其之前的構成乙個子段 dp[i-1]=0,所以sum=1。
2.第二個數同樣可以與之前組成乙個序列 =-1,或者自己單獨序列=-2,所以sum=-1
3.第三個數同樣 要麼-1與3,要麼3單獨,所以sum=3。
4.同理,第四個數0與-3 選擇 0 ,sum=0,第五個數 5與5 選擇5 sum=5
5.記錄過程中最大值的話,maxl=5,即構成的子段。
從過程中我們可以看出來,對於任何乙個i,我們可以根據 i-1的最優性,再對i的情況進行考慮,選擇最優。使得i成為最優,從而還可以推出i+1最優。
**:
ll n,m,k;
ll x,y;
ll num[maxn];
ll dp[maxn];
int main()
printf("%lld\n",maxl);
}return 0;
}
思考:如何輸出組成最大欄位和的數?
其實不難,我們只需要把過程倒著推回去即可。根據我們上述的推導過程,如果在dp[i]處最大子段和最大,那麼這個子段和是以i結尾的,所以這個子段和有兩種可能,第一種與num[i-1]相連 第二種自己相連。所以我們只需要判斷 i 處是不是由前面i-1推導而來即可,如果是由i-1推到而來,那麼它一定滿足 dp[i-1]=dp[i]-num[i]。否則他則是自己單獨成為子段。
所以**如下:
ll num[maxn];
ll dp[maxn];
bool vis[maxn];
void check()
}for(int i=minl;i<=k;i++)
printf("%lld ",num[i]);
}int main()
}ll ans=-inf;
for(int i=m;i<=n;i++)//從上圖我們也可以看出出現值是從i=m開始出現的
ans=max(ans,dp[m&1][i]);//判斷一下m為奇數還是偶數,m為偶數的話,最終結果應該在dp[0]內,否則dp[1]內,然後又剛好與與運算相同
printf("%lld\n",ans);
}return 0;
}
完結,當然動態規劃的優化確實是太有用,繼續加油,戒驕戒躁! 最大m子段和
最大m子段和問題 給定由n個整數 可能為負 組成的序列a1 a2 a3.an,以及乙個正整數m,要求確定序列的m個不想交子段,使這m個子段的總和最大!設b i,j 表示陣列a的前j項中i個子段和的最大值,並且第i個子段包含a j 1 i m,i j n 則所求的最優值為maxb m,j m j n ...
最大m子段和
51nod 1052 題意描述 給定陣列a,長度為n。給定整數m,求不相交的m段字段和的最大值。當m 1 時 該問題就是最大子段和問題。設dp i 為以a i 結尾的最大子段和,當我們考慮dp i 的時候如果dp i 1 0那麼肯定把a i 接在後面最優,否則,取a i 最優。得到 dp i max...
最大M子段和
最近掉入了dp的深淵,還附加數學知識,爽哉。在此分析一道提交了17次的dp n個整數組成的序列a 1 a 2 a 3 a n 將這n個數劃分為互不相交的m個子段,並且這m個子段的和是最大的。如果m n個數中正數的個數,那麼輸出所有正數的和。例如 2 11 4 13 5 6 2,分為2段,11 4 1...