题意: 给一个序列,把其分成不重复(合法的任意长度)的m段子序列,求最大的m段子段和。
思路: DP,由于序列长度达到了1e6,故还需优化空间。
dp[i][j]表示以j处元素结尾的i段子段和的最大值;
则状态转移方程:
(1)dp[i][j]=dp(i,j-1)+a(i) (即a[i]接到元素a[j-1]后面)
(2)dp[i][j]=dp(i-1,k)+a(i) (即a[i]自成一段,前面找一个i-1子段的最大值)
由于maxn达到了1e6,二维数组开不下;
故考虑滚动数组优化及预处理减少循环次数;
1.滚动数组优化:
由递推式子可知,dp状态矩阵中每个状态只与当前行和前一行有关;
故不存储整个状态转移矩阵,直接开两个数组计算当前行和前一行;
2.减少循环次数:
由于状态转移的第二种情况为找前一行的最大子段和;
故不循环遍历找,而是每次预处理max出来;
(algorithm库中的max函数默认是两个int值比较,当涉及到long long的比较时,要自己写一个,不然可能会出错)
代码如下:
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
const int maxn=1000050;
const ll INF=0x3f3f3f3f;
//dp[i][j]表示以j处元素结尾的i段子段和的最大值;
//则状态转移方程:
//(1)dp[i][j]=dp[i][j-1]+a[i](即a[i]接到元素a[j-1]后面)
//(2)dp[i][j]=dp[i-1][k]+a[i](即a[i]自成一段,前面找一个i-1子段的最大值)
//由于maxn达到了1e6,二维数组开不下;
//故考虑滚动数组优化及预处理减少循环次数;
//1.滚动数组优化:由递推式子可知,dp状态矩阵中每个状态只与当前行和前一行有关;
// 故不存储整个状态转移矩阵,直接开两个数组计算当前行和前一行;
//2.减少循环次数:由于状态转移的第二种情况为找前一行的最大子段和;
// 故不循环遍历找,而是每次预处理max出来;
ll dp[maxn];
ll last[maxn];//上一行的最大子段和;
ll a[maxn];
ll get_max(ll a,ll b)
{
return a>b?a:b;
}
int main()
{
int i,j;
int n,m;
while(~scanf("%d%d",&m,&n))
{
for(i=1;i<=n;i++)
{
scanf("%lld",&a[i]);
dp[i]=0;
last[i]=0;
}
//初始化:
dp[0]=0;
last[0]=0;
ll maxx;//每次记录当前行的最大子段和;
for(i=1;i<=m;i++)//遍历到m段;
{
maxx=-INF;
for(j=i;j<=n;j++)//从a[i]开始即可,因为至少i段;
{
dp[j]=get_max(last[j-1],dp[j-1])+a[j];
last[j-1]=maxx;
maxx=get_max(maxx,dp[j]);
}
}
printf("%lld\n",maxx);
}
return 0;
}