kuangbin專題十二 DP專題 HDU1024 最大m子序列和
阿新 • • 發佈:2019-01-29
題意:
給你n個數,然後讓你在裡面找到m個子序列,讓這m個子序列的和最大。其中可以不要一些數,但是這些子序列裡面的數必須是連續的。
題解:
這道題是我做kuangbin大神的專題的第一道題,當然就被嚇到不敢去做,今天用了快一天的時間把這道題給理解了,以下是我看的部落格:
http://blog.sina.com.cn/s/blog_677a3eb30100jxqa.html
http://blog.csdn.net/u013187393/article/details/42914165
http://blog.csdn.net/u013761036/article/details/39804595
按照順序來看比較好,好了,再在我這裡說一下我的見解吧,
這道題是最大子序列的和的加強版,比最大子序列噁心的多了,我之前的想法還停留在01揹包的層次,就是拿或者不拿的思維層次,發現在這裡行不通。所以學了一整天,然後我說一下公式:
dp[i][j]=max(dp[i][j-1]+a[j],dp[i-1][k]+a[j]),(其中i-1<=k<=j)。dp[i][j]就是把j個數分成i段的最大和,而dp[i][j-1]+a[j]就是把a[j]分配到第i段中,而後面的dp[i-1][k]+a[j]的意思就是把前面的j-1個數分成i-1段,而a[j]變成第i段的第一個數值。有可能說到這裡很多人不明白,這個k到底是怎麼回事吧?我們來看一下例子吧:
例如有一組資料 2 3 3 -7 2
那麼它的最大值是多少呢?答案是5,怎麼得出來的呢?
假如前面的dp值我們都已經計算出了,並且都是正確的,那麼dp[2][3]=max(dp[2][2]+a[3],dp[1][k]+a[3]),我們可以看出dp[2][2]為-4,那麼這裡k怎麼算呢?因為限制在i-1<=k<=j-1即是1<=k<=2中了,那麼我們看一下k=1的時候dp[1][1]為3,而k=2的時候dp[1][2]為-4,所以我們這時候當然是讓k=1了,帶入式子中就可以得出結果5了。為什麼k是取i-1<=k<=j-1的範圍呢?因為有時候a[j-1]可能是非常大的負數,大到使前面的正數值總和(我們位了好理解這裡假設前面的總和算出來是正數)加上該數也為負,你覺得你還會要它嗎?顯然不能,除非第i段缺少數值,才會要它,不到萬不得已是不會要它的。
然後這裡的m因為沒說出範圍所以我們要優化一下空間,即是dp[i][j]變成dp[j]表示的是第j個數時的最大值,以上是我的見解,剩下的例如時間上的優化可以看我發上面的三個部落格和我的程式碼註釋就好了。
#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
#define LL long long int
#define INF 0x7fffffff
const int MAXN= 1000000+7;
LL dp[MAXN];//因為不知道m是多少所以優化成了一維的了。
LL maxk[MAXN];//儲存的是分成幾段的最大值。
LL a[MAXN];
int main()
{
int n,m;
while(~scanf("%d%d",&m,&n))
{
for (int i=1;i<=n;i++)
{
scanf("%lld",&a[i]);
dp[i]=0;
maxk[i]=0;
}
LL MAX=-INF;
for(int i=1;i<=m;i++)
{
MAX=-INF;
for(int j=i;j<=n;j++)//為什麼是從i開始呢?因為你跑過了一遍分成i=1段之後,第i=1段就應該是最優的狀態了,所以不用再跑了,直接當做最優的狀態來參與分成i=2階段的計算。
{
dp[j]=max(dp[j-1]+a[j],maxk[j-1]+a[j]);//這裡就相當於dp[i][j]=max(dp[i][j-1]+a[j],dp[i-1][k]+a[j])。
maxk[j-1]=MAX;//這裡儲存的是dp[i-1][k]的值,即將k分成i-1段得到的最大值(i-1<=k<j-1). 還有就是不能更新mxk[j],只能更新j-1是因為更新j就會被當前的這個子序列更新的時候用到。
MAX=max(dp[j],MAX);//儲存最大值。
}
}
printf("%lld\n",MAX);//還有就是這裡不能寫成dp[n],因為dp[j]代表的是到第j個的時候的最大和。例如你計算到最後,有可能不要最後一個數比要最後一個數要大。所以這裡要輸出MAX。
}
}