http://acm.hdu.edu.cn/showproblem.php?pid=1024
自己也是没想出怎么做,看了解题报告。
做完之后对DP能够有更深的理解。
求n个数分为m段的最大和。
首先比较难想到的是递推公式。
假设j个数分为i段,求最大和,第j个数为a[ j ],当前状态为dp[ i ] [ j]。并且我们假定第j个数被分在最后一段中。
那么,可以分为两种情况: 1.第j个数与其前面的几个数共同成段 2.第j个数单独成段
对于第一种情况,dp[ i ][j] = dp[ i ] [ j - 1 ] + a[ j ]。 对于第二种情况 dp[ i ][ j ] = max( dp[ i - 1 ] [ t ] ) + a[ j ] , ( i - 1 <= t <= j - 1 )
由于是求最大,所以可得 dp[ i ] [ j ] = max(dp[ i ] [ j - 1],max( dp[ i - 1][ t ] ) ) + a[ j ]
接下来做第一次压缩:
我们设法省去 max( dp[ i - 1][ t ] ) ,( i - 1 <= t <= j - 1 ) 对 t 的遍历。 只要在计算dp[i - 1][ ] 对j遍历时 保存 每个 j 值 所对应的 max( dp[ i - 1][ t ] ) 于pre[ i ] [ j ] 中
所以我们这样做:
对于每个i,我们遍历j并且计算dp[ i ] [ j ] 的值,用于计算dp[ i ] [ j+1]。
并且保存每个j值所对应的当前 max( dp[ i ] [ t ] ) , ( i <= t <= j ) 于pre[ i ] [ j ] ,用于计算 dp[ i + 1] [ j ]
for(i = 1; i <= m; i++)
{
maxx = -100000000;//注意对于每个i都要初始化 maxx
for(j = i; j <= n; j++)
{
dp[i][j] = max(dp[i][j - 1],pre[i - 1][j] ) + a[j];
maxx = max( maxx, dp[i][j]); //注意这一行和上面一行 的顺序,不能对调。 因为max( dp[i][t])中 t的上界 是 j - 1。如果对调,上界变成 j
}
}
上面的这次压缩是对时间的压缩,时间复杂度从 o(n^3) 变为 o(n^2)。
然后我们对空间进行压缩:
我们发现,对于一个特定的i ,计算 dp[ i ][ ] 时,我们只用到 dp[ i ][ ] ,而和 dp[ i - 1][ ],dp[ i - 2][ ]的值都无关 。
同时我们只用到 pre[i - 1][ ] ,而和 pre[ i - 2 ],pre[ i - 3 ] 都无关。
所以我们每次只要 保存 dp[ i ][ ] 这一组值,在计算dp[ i + 1][ ]时,就已经可以舍弃 dp[ i ][ ]了。
同样的我们只要保存 pre[ i - 1 ][ ] 这一组值就可以,计算dp[ i + 1] 时 ,就已经可以舍弃pre[ i - 1 ][ ] 了。
即,计算dp[ 3 ][ ] 时 舍弃 dp[ 2 ][ ]保存dp[ 3 ][ ],计算dp[ 4 ][ ] 时 舍弃 dp[ 3 ][ ]保存dp[ 4 ][ ]。
我们可以看到,每次保存的只是 一组数,dp根本没必要使用二维数组了。同样的,pre也不用二维数组了。
用dp[ j ]保存当前状态,在计算dp[ j + 1] 时调用,在遍历到下一个 i 时,用下一个dp[ j ]覆盖。
pre[ j ] 保存最大值,在计算dp[ j + 1 ] 时使用,在下一个i时 覆盖。
#include<stdio.h>
#include<math.h>
#include<stdlib.h>
#include<string.h>
#define maxn 1000003
int a[maxn],m,n,dp[maxn],pr[maxn];
int max(int a,int b)
{
return a>b?a:b;
}
int main()
{
int i,j,maxx;
while(~scanf("%d%d",&m,&n))
{
memset(pr,0,sizeof(pr));
for(i = 1; i <= n; i++)
{
scanf("%d",&a[i]);
}
for(i = 1; i <= m; i++)
{
maxx = -100000000;
for(j = i; j <= n; j++)
{
dp[j] = max(pr[j - 1],dp[j - 1]) + a[j];
pr[j - 1] = maxx;
maxx = max(maxx,dp[j]);
}
}
printf("%d\n",maxx);
}
return 0;
}
本文探讨了一个典型的DP问题——将n个数分成m段以获得最大和,并详细阐述了解决该问题的方法,包括递推公式的推导、时间复杂度从O(n^3)到O(n^2)的优化过程及空间复杂度的降低。
1227

被折叠的 条评论
为什么被折叠?



