Max Sum Plus Plus HDU - 1024

原题链接

考察:线性dp

错误思路:

        f[i][j]表示前i个字符形成j段的和.此时分为两种情况:

  1. 不选a[i],f[i][j] = f[i-1][j]
  2. 选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 }

 

posted @ 2021-03-13 07:41  acmloser  阅读(28)  评论(0编辑  收藏  举报