hdu-1024 多段子段最大和问题

来源:互联网 发布:linux把ip写入配置文件 编辑:程序博客网 时间:2024/06/01 16:39

典型动态规划题目  链接 hdu-1024

题目大概意思是在长度为 i 的序列中找出 j 段连续子序列,并且这 j 段序列元素的和为最大值。

假设S[i]存放的是题目给出的序列,不难发现,总共也只有两种情况:1,若从前 i 段中选 j 段出来最后一段的最后一个元素是 S[i] 的话,若S[i+1]>=0,S[i+1]直接并入最后一段就行,若S[i+1]<0,跳过这一个元素。 2,若最后一个元素不是S[i],则dp[i][j]=max(dp[k][j-1]+maxsum(k+1,i))1<=k<i,意思是从前k个元素中找出j-1段,再在第k+1到第i个元素中找出最后一段序列,这里k需要每一次都要从 1 到 i-1 变化才能保证每次找出最大序列而maxsum(i,j)代表的是找到从第 i 个到第 j 元素间的最大子序列和,最快算法时间复杂度为O(n),对于第一种情况时间复杂度为O(1),第二种情况为O(n^2),加上 i 的迭代,总的时间复杂度将为O(n^3),当然会超。

如果直接常规性的用dp[i][j]这样来存放和计算的话内存和时间都将超出,空间复杂度为O(n^2),而n的范围为[1,100万],内存也会超。

现在引入数组 q,q[j]代表从前 i 个元素中选 j 段出来并且最后一段结尾元素一定为S[i] 的最大值(这个道理和从简单的从n个元素中找1段的算法是一个道理)

再创建数组 p 代替数组dp,p[j]代表从前 i 个元素中选 j 段的最大和,而把 i 放到最外层迭代,和从n个元素中找一段类似。

显然p[j]>=q[j],也就是从前 i 段中选 j 段的最大和。

先直接贴出代码:

#include<iostream>#include<cstring>using namespace std;#define mem(a) memset(a,0,sizeof(a))#define max(a,b) (a>b?a:b)#define min(a,b) (a<b?a:b)#define MaxN 1000050int S[MaxN],p[MaxN],q[MaxN];int main(){   int N,m;   while(cin>>m>>N){      for(int i=1;i<=N;i++){         cin>>S[i];      }      mem(p);mem(q);//p[j]代表从前i个中选j段的最大值  q[j]代表从前i段中选j段并且一定要有S[i]在内的最大值      p[1]=q[1]=S[1];      for(int i=1;i<=N;i++){         for(int j=min(i,m);j>0;j--){            if(j==i)p[j]=q[j]=p[j-1]+S[i];            else{               q[j]=max(q[j]+S[i],p[j-1]+S[i]);               p[j]=max(p[j],q[j]);            }         }      }      cout<<p[m]<<endl;   }}
当最外层的 i-1 增加1变成 i 后,新的循环开始执行,为了方便理解先添加上 i 下标,q[i][j]=max(q[i-1][j]+S[i],p[i-1][j-1]+S[i]),p[i][j]=max(p[i-1][j],q[i][j])

p[i][j]的值要么为舍弃S[i]后的p[i-1][j]或者不舍弃S[i]从前k个元素中选j-1段再从k+1到 i 中选出最后一段的q[i][j],而q[i][j]要么为直接吧S[i]并入最后一段的q[i-1][j]+S[i]要么为从前k个中选出 j-1 段然后S[i]单独最为一段。

而在每次 i 增加1循环还没开始执行前,q[j],p[j]中存放的是q[i-1][j],p[i-1][j],执行后存放的是q[i][j],p[i][j]。因为每次更新需要用到q[i-1][j],p[i-1][j],所以q[i-1],p[i-1]的更新需要在q[i],p[i]的更新之后。所以上面是倒序更新的而p[i-1][j],q[i-1][j]在p[i][j],q[i][j]更新之后就没用了,所以直接用p[i-1][j],q[i-1][j]的空间来存放p[i][j],q[i][j]。也就只需要写p[i],q[i]了