首先申明:这题数据有很大问题
当你输入都为负数时,程序就会出错
如 2 3
-1 -2 -3
竟然输出-3。
数据库里有都为负数的情况,我用别人AC代码改了一下测试过了,的确有这情况,但答案貌似是错的。
又如 2 3
-1 2 -3
竟然输出1.
这让我郁闷了很久。
设输入的数组为a[1...n],从中找出m个段,使者几个段的和为最大
dp[i][j]表示前j个数中取i个段的和的最大值,其中最后一个段包含a[j]。(这很关键)
则状态转移方程为:
dp[i][j]=max{dp[i][j-1]+a[j],max{dp[i-1][t]}+a[j]} i-1=<t<j-1
因为dp[i][j]中a[j]可能就自身一个数组成最后一段,或者a[j]与a[j-1]等前面的数组成最后一段。
此题n数据太大,二维数组开不下,而且三重循环,想到状态转移方程后还是困难重重。
想想,二维数组不行的话,肯定要压缩成一维数组:
因为dp[i-1][t]的值只在计算dp[i][j]的时候用到,那么没有必要保存所有的dp[i][j] for i=1 to m,这样我们可以用一维数组存储。
用pre[j]表示j之前一个状态dp[i-1][]中1-j之间,不一定包含a[j]的最大字段和,然后推dp[i][j]状态时,dp[i][j]=max{pre[j-1],dp[j-1]}+a[j];
褐色的为了方便理解,其实不存在。
AC代码:

#include<stdio.h> #include<string.h> #include<algorithm> using namespace std; #define maxn 1000003 #define INF 100000000 int dp[maxn],a[maxn],pre[maxn]; int main() { int n, m, i, j, maxx; while(~scanf("%d%d",&m,&n)) { for(i=1;i<=n;i++) scanf("%d",&a[i]); dp[0]=0; memset(pre,0,sizeof(pre)); for(i=1;i<=m;i++) { maxx =-INF; for(j=i;j<=n;j++)//对于每个i,随着j的增大,maxx越滚越大 { dp[j]=max(dp[j-1],pre[j-1])+a[j]; pre[j-1]=maxx;//把前一轮的最大值赋给pre; //printf("dp[%d]=%d pre[%d]=%d\n",j,dp[j],j-1,pre[j-1]); if(maxx<dp[j])maxx=dp[j]; } //puts(""); } printf("%d\n",maxx);//最后一轮的最大值就是答案。 //因为上一个循环中对于每个i,随着j的增大,maxx越滚越大 } //而且pre也是越滚越大的。 return 0; } //给了一组数据,不理解就把所有DP打出来,自己手动模拟一遍,这样好理解多了 /* 4 7 1 2 -4 5 6 -8 10 */
本人觉得正确的代码,可能有错误:

#include<stdio.h> #include<string.h> #include<algorithm> using namespace std; #define maxn 1000003 #define INF 100000000 int dp[maxn],a[maxn],pre[maxn]; int main() { int n, m, i, j, maxx,ans; while(~scanf("%d%d",&m,&n)) { for(i=1;i<=n;i++) scanf("%d",&a[i]); dp[0]=0;ans=-INF; memset(pre,0,sizeof(pre)); for(i=1;i<=m;i++) { maxx =-INF; for(j=i;j<=n;j++)//对于每个i( 每组),随着j的增大,maxx越滚越大 ,但i->i+1后,有些maxx不一定越滚越大 { dp[j]=max(dp[j-1],pre[j-1])+a[j]; pre[j-1]=maxx;//把前一轮的最大值赋给pre; //printf("dp[%d]=%d pre[%d]=%d ",j,dp[j],j-1,pre[j-1]); if(maxx<dp[j])maxx=dp[j]; //printf("maxx=%d\n",maxx); } if(ans<maxx)ans=maxx; //puts(""); } printf("%d\n",ans); } return 0; }