Max Sum Plus Plus HDU - 1024
考察:线性dp
错误思路:
f[i][j]表示前i个字符形成j段的和.此时分为两种情况:
- 不选a[i],f[i][j] = f[i-1][j]
- 选a[i]->a[i]为独立的一段,f[i][j] = f[i-1][j-1]->a[i]不为独立的一段,此时的条件是a[i-1]也在第i段中,状态不太好表示,下面的更容易理解点
正确思路:
f[i][j]表示前i个字符形成j段的和,且第i段结尾a[i].
那么划分集合是依据a[i]是否为独立的一段,如果不是f[i][j] = f[i-1][j]+a[i],如果是f[i][j] = maxf[k][j-1]+a[i]//此时j-1的结尾不确定,需要枚举.
此时的时间复杂度是O(n3),而且会MLE.
根据分级那道题可知,f[k][j-1]只与k,j有关.代表上一阶段的结尾k的最大值.可以用变量边求边存.
但是这样还是会MLE.考虑将压缩为一维.由状态转移方程可知,我们要存的状态是f[i-1][j]和maxf[k][j-1].前者很容易压缩,直接去掉j形成一维f[i] = f[i-1]+a[i].后者的本质意义是k = j~i-1范围内f[k][j-1]上一阶段的最小值.因为k的范围不定,所以需要一维数组存储.我们可以先使用上一阶段的值,用完就更新为本阶段的值.那么pre就是上一阶段的最值.
还有要注意的是,当j==m时,i>=j才有意义,所以求答案要从j=m开始枚举
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 using namespace std; 5 typedef long long LL; 6 const int N = 1000010; 7 int n,m,a[N],f[N],pre[N]; 8 int main() 9 { 10 while(scanf("%d%d",&m,&n)!=EOF) 11 { 12 for(int i=1;i<=n;i++) scanf("%d",&a[i]),f[i] = -1e9; 13 int maxn; 14 memset(f,0,sizeof f); 15 memset(pre,0,sizeof pre);//初始条件是f[i][j-1]在j~i-1的最大值,pre的初值就是f[i][0] 16 for(int j=1;j<=m;j++) 17 { 18 maxn = -1e9; 19 for(int i=j;i<=n;i++) 20 { 21 f[i] = max(pre[i-1]+a[i],f[i-1]+a[i]); 22 pre[i-1] = maxn; 23 maxn = max(f[i],maxn); 24 }//当j==m时,存在不合法的状态 ,i<m该状态不存在 25 } 26 printf("%d\n",maxn); 27 } 28 return 0; 29 }