题目背景
隔壁的新初一电脑班刚考过一场试,又到了BlingBling的裁员时间,老师把这项工作交给了ZZY来进行。而ZZY最近忙着刷题,就把这重要的任务交(tui)给了你。
题目描述
ZZY有独特的裁员技巧:每个同学都有一个考试得分ai(-1000<=ai<=1000),在n个同学(n<=500)中选出不大于k段(k<=n)相邻的同学留下,裁掉未被选中的同学,使剩下同学的得分和最大。要特别注意的是,这次考试答错要扣分【不要问我为什么】,所以得分有可能为负。
输入输出格式
输入格式:第一行为n,k,第二行为第1~n位同学的得分。
一个数s,为最大得分和。
解法:
我们设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