hdoj1024 Max Sum Plus Plus (DP)

来源:互联网 发布:yum install gcc 失败 编辑:程序博客网 时间:2024/06/08 08:02

题目:http://acm.hdu.edu.cn/showproblem.php?pid=1024

题意:

给一个数字序列a1,a2,a3,...,an,(n<=1000000),求从中取出m个子序列的最大和,单个子序列必须是连续的,而且各个子序列之间没有重叠部分;

输入格式为:m  n  a1  a2  a3  ...  an

Sample Input
1   3   1   2   3
2   6  -1   4  -2  3  -2   3
Sample Output
6
8

思路:

        dp[m][n]=max( dp[m][n-1]+s[n] ,dp[m-1][t]+s[n]),其中m-1<=t<=n-1;dp[m][n]表示前n列数据中选m个子段的最大和,第n列一定要包含在序列中,(原因很简单,如果不包含在其中,那么就会出现dp[m][n]=dp[m][x],x<n;也就是说数据是重复的,而且状态转移方程也会发生变化,不再是上述方程)dp[m][n-1]+s[n]表示前n-1列中取m个子段,s[n-1]是在第m段中的,s[n]放入第m段即可;dp[m-1][t]+s[n]表示将s[n]单独做为第m段,因此还要在前面的序列中找m-1个子段,而这m-1个子段可能是dp[m-1][t]中的任意一个(m-1<=t<=n-1);s[n]存输入的序列,sum[n]表示前n项和,由于在求dp[m][n]时要用到 max(dp[m-1][m-1],...,dp[m-1][n-1]),也就是dp[m-1][t]中的最大值,因此用dp_low[n-1]来存取m-1到n-1列中取m-1子段的最大值,而dp_high[n]就存取n列中取m子段的最大值;由于每次求dp[m][n]时都要用到它的前一个状态dp[m-1][n-1];于是先从它的初始状态着手:
        当m=1时,基本状态方程为dp[j]=max(dp[j-1],0)+s[j],(dp[0]=0,1<=j<=n);依此得到dp_high[j]=max(dp_high[j-1],0)+s[j],(dp_high[0]=0,1<=j<=n),dp_low[j]=max( dp_high[j],dp_low[j-1] ),(dp_low[0]=INI_MIN);只要得到了初始的dp_low[],求dp_high[]就容易了。
        当m>1时,前j列中取i个子段,当i==j时,dp_high[j]=sum[j];当j>i时,dp_high[j]=max(dp_high[j-1],dp_low[j-1])+s[j],dp_high[j-1]表示j-1列取i个子段的最大值,dp_low[j-1]表示i-1到j-1列取i-1个子段的最大值,算出dp_high[j]的值后需要立即更dp_low[j-1]的值为取i个子段,以供取i+1个子段时dp_high[j]使用,dp_low[j-1]=max(dp_high[j-1],dp_low[j-2]),要考虑j的临界取值情况。当求出dp_high[n]时,只需要更新到dp_low[n-1],也就是用不到dp_low[n],这里仍然要算出db_low[n]来存放i到n列选i个子段时的最大值,最后选m子段的结果就存放在db_low[n]中。

#include <stdio.h>int s[1000009];int dp_high[1000009];int dp_low[1000009];int sum[1000009];int max(int a,int b){if(a>b)return a;else return b;}int main(){int m,n,i,j;sum[0]=0;while(scanf("%d %d",&m,&n)!=EOF){memset(dp_high,0,sizeof(dp_high));memset(dp_low,0,sizeof(dp_low));dp_low[0]=-100000000;for(i=1;i<=n;i++){scanf("%d",&s[i]);sum[i]=sum[i-1]+s[i];dp_high[i]=max(dp_high[i-1],0)+s[i];dp_low[i]=max(dp_high[i],dp_low[i-1]);}for(i=2;i<=m;i++){dp_high[i]=sum[i];for(j=i+1;j<=n;j++){dp_high[j]=max(dp_high[j-1],dp_low[j-1])+s[j];if(j==i+1){dp_low[j-1]=dp_high[j-1];}else{dp_low[j-1]=max(dp_high[j-1],dp_low[j-2]);}}if(j==i+1){dp_low[j-1]=dp_high[j-1];}else{dp_low[j-1]=max(dp_high[j-1],dp_low[j-2]);}}printf("%d\n",dp_low[n]);}return 0;}

代码优化:

#include <stdio.h>int s[1000009];int dp[1000009];int sum[1000009];int t_max[1000009];int max(int a,int b){return a>b?a:b;}int main(){int m,n,i,j;while(scanf("%d %d",&m,&n)!=EOF){memset(dp,0,sizeof(dp));memset(t_max,0,sizeof(t_max));sum[0]=0;for(i=1;i<=n;i++){scanf("%d",&s[i]);sum[i]=sum[i-1]+s[i];}for(i=1;i<=m;i++){dp[i]=sum[i];t_max[i-1]=-1000000000; //后面代码中t_max[j-2]有时表示前x列取x+1个子段,这里赋极小值做特殊数据处理for(j=i+1;j<=n;j++){dp[j]=max(dp[j-1],t_max[j-1])+s[j];//t_max[j-1]为i-1到j-1列取i-1个子段最大值t_max[j-1]=max(dp[j-1],t_max[j-2]); //t_max[j-1]为i-1到j-1列取i个子段最大值}t_max[j-1]=max(dp[j-1],t_max[j-2]);  //此处j-1==n,可理解为前i-1到n列取i个子段最大值}printf("%d\n",t_max[n]);}return 0;}



原创粉丝点击