解法:
我们设
f[i][j][0/1]
f
[
i
]
[
j
]
[
0
/
1
]
表示将前 i 个人分成 j 组,不保留/保留 i 的最大得分和。若不保留 i ,则当前状态等于 i-1 保留/不保留,分成 j 组的最大得分和(没有保留 i,可以看做分组没有断开);若保留 i ,则当前状态等于保留 i-1,分成 j 组和不保留 i-1,分成 j-1 组(i 保留而 i-1 没有保留,分组断开)的最大值加上 i 的得分。
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
const int N=510;
int n,m,ans,f[N][N][2];
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
{
int x;scanf("%d",&x);
for(int j=1;j<=min(i,m);j++)
{
f[i][j][0]=max(f[i-1][j][0],f[i-1][j][1]);//不保留i时的转移
f[i][j][1]=max(f[i-1][j-1][0],f[i-1][j][1])+x;//保留i时的转移
}
}
ans=max(f[n][1][0],f[n][1][1]);
for(int i=2;i<=m;i++)ans=max(ans,max(f[n][i][0],f[n][i][1]));//答案取所有的最大值
printf("%d\n",ans);return 0;
}
优化:
我们会发现 i 的所有状态都只从 i-1 的相应状态转移过来,所以我们可以像01背包的优化那样,省掉一维,优化一下空间。代码:
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
const int N=510;
int n,m,ans,f[N][2];
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
{
int x;scanf("%d",&x);
for(int j=min(i,m);j>0;j--)//注意j是从大到小,因为更新f[i][j]要用到f[i-1][j-1],不能覆盖掉
{
f[j][0]=max(f[j][0],f[j][1]);
f[j][1]=max(f[j-1][0],f[j][1])+x;//转移是一样哒
}
}
ans=max(f[1][0],f[1][1]);
for(int i=2;i<=m;i++)ans=max(ans,max(f[i][0],f[i][1]));
printf("%d\n",ans);return 0;
}
洛谷实测空间消耗少了将近一半啊qwq