洛谷P2389 电脑班的裁员

题目背景

隔壁的新初一电脑班刚考过一场试,又到了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

猜你喜欢

转载自blog.csdn.net/kdlkswb/article/details/80681409