题目链接:https://vjudge.net/problem/HDU-1024
解题思路:自己完全没有思路,于是去看了kuangbin大神的题解,看了半天才大概理解了,自己写了一版,然后发现跟题解几乎一毛一样。。。这一题得详详细细地写一篇题解,好好地思考总结一下。
用num[]数组保存数据,n为数组中的元素总数,m为段数。
dp[i][j]:在选取第j个数的前提下,将前j个数分成i段所能得到的最大值。
则对于dp[i][j],有两种决策:1、将第j个数作为第i段的最后一个数(也即让其并在第i段上,并且第i段的最后一个数为第j-1个数;2、将第j个数作为第i段的第一个数(也即让其单独成段)。
状态转移方程:dp[i][j]=max(dp[i][j-1] + num[j], max(dp[i-1][k] | 0<k<j) + num[j]);
但是,因为1≤n≤1,000,000而且m的数据范围未知,我们很难开出这么大的dp[][]数组,这个时候需要用到滚动数组的技巧。先上核心代码:
1 for(i=1;i<=m;i++) 2 { 3 mmmax=-INF; //对于每一个i,把 mmmax 初始为-INF,一方面,这个 mmmax 可以用来保存对于外层循环的这一个 i 值,把前 j-1 个数分成 i 段所能得到的最大值。 4 for(j=i;j<=n;j++) 5 { 6 dp[j]=max(dp[j-1]+a[j],mmax[j-1]+a[j]); // dp[j-1] 相当于 dp[i][j-1] ,而 mmax[j-1] 即为 max(dp[i-1][k] | 0<k<j) , mmax[]数组利用记忆化的方式,方便地 7 // 实现了这个功能。 8 9 mmax[j-1]=mmmax; //另一方面, mmmax 也起到一个延迟标记的作用,对于内层循环的这一个 j 值 mmmax 保存了把前 j-1 个数分成 i-1 段所能得到的最大值(即直到 10 mmmax=max(mmmax,dp[j]); //j对应的数的上一个数的最大值),用 mmmax 更新mmax[j-1],可使对于 j+1 (即内层循环的下一次操作),用到的 mmax[j+1-1]是当i = i-1 11 //(即外层循环的上一次操作)时所对应的 mmax[j] 12 13 } 14 }
至此,这道题就讲的差不多了。
AC代码(作者:kuangbin)
1 #include<stdio.h> 2 #include<algorithm> 3 #include<iostream> 4 using namespace std; 5 #define MAXN 1000000 6 #define INF 0x7fffffff 7 int dp[MAXN+10]; 8 int mmax[MAXN+10]; 9 int a[MAXN+10]; 10 int main() 11 { 12 int n,m; 13 int i,j,mmmax; 14 while(scanf("%d%d",&m,&n)!=EOF) 15 { 16 for(i=1;i<=n;i++) 17 { 18 scanf("%d",&a[i]); 19 mmax[i]=0; 20 dp[i]=0; 21 } 22 dp[0]=0; 23 mmax[0]=0; 24 for(i=1;i<=m;i++) 25 { 26 mmmax=-INF; 27 for(j=i;j<=n;j++) 28 { 29 dp[j]=max(dp[j-1]+a[j],mmax[j-1]+a[j]); 30 mmax[j-1]=mmmax; 31 mmmax=max(mmmax,dp[j]); 32 } 33 } 34 printf("%d ",mmmax); 35 36 } 37 return 0; 38 }
附上kuangbin签名:人一我百!人十我万!永不放弃~~~怀着自信的心,去追逐梦想!