电脑班的裁员(区间DP)

|

题目背景

隔壁的新初一电脑班刚考过一场试,又到了BlingBling的裁员时间,老师把这项工作交给了ZZY来进行。而ZZY最近忙着刷题,就把这重要的任务交(tui)给了你。
题目描述

ZZY有独特的裁员技巧:每个同学都有一个考试得分$ai(-1000<=ai<=1000)$,在n个同学$(n<=500)$中选出不大于k段$(k<=n)$相邻的同学留下,裁掉未被选中的同学,使剩下同学的得分和最大。要特别注意的是,这次考试答错要扣分【不要问我为什么】,所以得分有可能为负。

输入输出格式

输入格式:

第一行为n,k,第二行为第1~n位同学的得分。

输出格式:

一个数s,为最大得分和。

—————————我是分割线—————————–

强力安利出题人写的题解,里面竟然有$O(N)$的做法!

我这个蒟蒻不才,只好写一下$O(N^3)$的DP维持一下生活。

首先考虑建模,$f[i][j]$表示前i个数分成最多j个区间的最大价值,那么我们就可以枚举第j个区间的起点k,

那么如果 $(k,i)>=0$ ,状态转移式子为: $f[i][j]=max(f[i][j],f[k-1][j-1]+sum(k,i))$

如果 $(k,i)<0$ 就不选这个区间,式子为:$f[i][j]=max(f[i][j],f[k-1][j])$

最后还有一个点,如果整个数列没有一个正数,答案就是0

贴代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
int n,k,a[501],s[501],i,j,m;
int f[501][501];
int main(){
scanf("%d%d",&n,&m);
for (i=1; i<=n; i++){
scanf("%d",&a[i]);
s[i]=s[i-1]+a[i];
}
memset(f,-10,sizeof(f));
for (i=1; i<=n; i++){
for (j=1; j<=i; j++)
for (k=1; k<=j; k++)
f[i][1]=max(f[i][1],s[j]-s[k-1]);
}
for (i=1; i<=n; i++)
for (j=2; j<=i; j++){
for (k=j; k<=i; k++)
if (s[i]-s[k-1]>=0)
f[i][j]=max(f[i][j],f[k-1][j-1]+s[i]-s[k-1]);
else f[i][j]=max(f[i][j],f[k-1][j]);
f[i][j]=max(f[i][j],f[i][j-1]);
}
printf("%d",max(f[n][m],0));
return 0;
}

文章目录
,
载入天数...载入时分秒... 字数统计:75.7k