上篇已经稍微介绍了什么是dp,接下来就是实战了。今天七夕节,祝愿大家有人陪伴,好好珍惜。。哎。。
给定一个数字序列A1,A2,A3,A4,A5,A6,A7,A8,A9.....An。求i,j(1<=i<=j<=n)使得Ai++++++Ai最大,输出这个最大和
样例:
-2 11 -4 13 -5 -2
显然11+(-4)+13=20为和最大的选取情况,因此最大和为20.
下面介绍动态的做法,复杂度O(n),我们会发现其实左端点的枚举是没有必要的。。
步骤1:令状态dp[i]表示以A[i]作为末尾的连续序列的最大和(这里是说A[i]必须作为连续序列的末尾)。以样例为列:序列
-2 11 -4 13 -5 2,下标分别记为0,1,2,3,4,5,那么
dp[0]=-2;
dp[1]=11
dp[2]=(11+(-4))=7;
dp[3]=(11+(-4)+13)=20
dp[4]=(11+(-4)+13+(-5))=15
dp[5]=13;
步骤2:做如下考虑:因为dp[i]要求是必须A[i]结尾的连续序列,那么只有两种情况
1.这个最大和的连续序列只有一个元素,即以A[i]开始,以A[i]结尾。
2.这个最大和连续序列有多个元素,即从前面某处A[p]开始(p<i),一直到A[i]结尾。
对于第一种情况,最大和就是A[i]本身。
对于第二种情况,最大和就是dp[i-1]+A[i];
由于只有这两种情况,于是得到转移转移方程;
dp[i]=MAX{dp[i],dp[i-1]+A[i]}
这个式子只和i与i以前的元素有关,且边界dp[0]=A[0],由此从小到大枚举i,即可得到整个dp数组,接着输出dp[0],dp[1],....dp[n-1]中的最大值即为最大连续序列的和。
-------------------------code---------------------------------------------------------
int A[maxn],dp[maxn];//从A[i]存放序列,dp[i]存放以A[i]结尾A[i]结尾的连续序列的最大和。
int main(){
int n;
scanf("%d",&n);
for(int i=0;i<n;i++)
scanf("%d",&A[i]);
//边界
dp[0]=A[0];
for(int i=1;i<n;i++)
//状态转移方程
dp[i]=max(A[i],dp[i-1]+A[i]);
//dp[i]存放以A[i]结尾的连续序列的最大和,需要遍历i得到的才是结果。
int k=0;
for(int i=1;i<n;i++){
if(dp[i]>dp[k]){
k=i;
}
}
printf("%d\n",dp[k]);
}
//废话一句,至于让我们去求这里的连续序列的话,我们已经求出下边啦,那么久知道A[i]的下标,一个While(dp[k]!=0),每次都遍历往前减。
事实上,如何设计状态和状态转移方程,才是动态规划的核心。