描述
题解
不得不说,这是一道十分有趣的dp,同时也刷新了我对dp的认知,我原来的思维过于局限了。
以下为某大牛思路:
用两个数组,pre[MAXN]和dp[MAXN],
首先m次循环,第x次循环代表的是把整个序列分成x个子段所能得到的最大x子段和。
pre[i]数组记录的是,从第1个数到第i个数被分成x个子段所能得到的最大子段和。
假设当前已分成了x个子段(即最外层循环执行了x次),然后需要执行第x+1次时,
则dp[i]的转移是从下列两种情况取max值:
1.前i-1个数分为x个子段得到的最大和pre[i-1]加上单独把input[i]作为第x+1个子段的开头;
2.从前i-1个数中已经分出了x+1个子段,且第x+1个子段的尾部需要加上input[i],即dp[i-1]+input[i]。
是时候来一场dp的专项训练了~(≧▽≦)/~
代码
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
const int MAXN = 5050;
const int MIN_INF = -0x3f3f3f3f;
ll dp[MAXN];
ll input[MAXN];
ll pre[MAXN];
int n, m;
int main()
{
scanf("%d%d", &n, &m);
dp[0] = MIN_INF;
pre[0] = 0;
for (int i = 1; i <= n; i++)
{
scanf("%lld", &input[i]);
pre[i] = 0;
dp[i] = MIN_INF;
}
while (m--)
{
ll ans = MIN_INF;
for (int i = 1; i <= n; i++)
{
if (i == 1)
{
dp[i] = pre[i - 1] + input[i];
}
else
{
if (dp[i - 1] > pre[i - 1])
{
dp[i] = dp[i - 1] + input[i]; // 第i个数与它的上一个数在同一子段内
}
else
{
dp[i] = pre[i - 1] + input[i]; // 从第i个分出一个新的子段
}
}
// dp[i] = max(dp[i - 1], pre[i - 1]) + input[i]; 或者直接这么取值也可
pre[i - 1] = ans;
ans = max(ans, dp[i]);
}
pre[n] = ans;
}
printf("%lld\n", pre[n]);
return 0;
}