题目描述:
在 L 的书架上,有 N 本精彩绝伦的书籍,每本书价值不菲。
M 是一个书籍爱好者,他对 L 的书籍早就垂涎三尺。最后他忍受不了诱惑,觉得去偷 L 的书,为了迅速完成这件事,同时他不希望 L 很快发现书籍少了,他决定偷书时,对于任意连续的 K 本书,他最多选 B 本,最少选 A 本。现在他想知道怎么选出来的书本最后使得偷的书籍的价值和,与剩下的书籍价值和,差值最大。
输入格式:
第一行四个整数 N , K , A , B
一行 N 个整数表示每本书的价值
输出格式:
一个整数表示答案
样例数据:
输入
2 1 0 1
2 -2
输出
4
备注:
【样例解释】
得到第一本书 得到的价值和是 2
剩余的价值和是-2
差值为 4
【数据范围】
对于 20%:N ≤ 10
对于另外 20%:A = 0 , B = K
对于 100%:N ≤ 1000 , 0 ≤ A ≤ B ≤ K ≤ 10,所有书籍的价值的绝对值 ≤
自己写状压写对了耶,好happy
本来这道题准备些N<=10的暴力的
然后写了个回溯,想一想觉得太麻烦,于是就改成枚举状态
既然N<=10 可以枚举状态,我们每次偷的书只与前k个有影响
k<=10 ,然后我就莫名其妙地想到了状压
f[i][state] 表示 偷到i,前面k个的状态(偷没偷)
所以说一定不要因为没有想到正解就不写,
在写部分分的时候说不定就想到了正解呢
#include<bits/stdc++.h>
#define N 1005
#define M 1<<10
#define LL long long
#define inf 100000000000000
using namespace std;
int n,k,a,b,x[N];
LL f[N][M],ans,sum;
int read(){
int cnt=0,f=1;char ch=0;
while(!isdigit(ch)){ch=getchar();if(ch=='-')f=-1;}
while(isdigit(ch))cnt=cnt*10+(ch-'0'),ch=getchar();
return cnt*f;
}
int count(int j){
int ret=0;
for(;j;j>>=1)
if(j&1) ret++;
return ret;
}
int main(){
freopen("grape.in","r",stdin);
freopen("grape.out","w",stdout);
n=read(),k=read(),a=read(),b=read();
for(int i=1;i<=n;i++)
for(int j=1;j<1<<k;j++)
f[i][j]=-inf;
for(int i=1;i<=n;i++){
x[i]=read();sum+=x[i];
}
for(int i=1;i<=k;i++){
for(int j=1;j<1<<(min(i,k));j++){
f[i][j]=0;
int p=j,ret=0;
for(;p;p>>=1){
if(p&1) f[i][j]+=2*x[i-ret];ret++;
}
}
}
for(int i=k;i<=n;i++){
for(int j=0;j<1<<k;j++){
int ret=count(j);
if(ret>=a&&ret<=b){
int cur=j<<1;
if(j&(1<<(k-1))){
cur-=1<<k;
if(ret!=a) f[i+1][cur]=max(f[i+1][cur],f[i][j]);
f[i+1][cur|1]=max(f[i+1][cur|1],f[i][j]+2*x[i+1]);
}
else{
f[i+1][cur]=max(f[i+1][cur],f[i][j]);
if(ret!=b){
f[i+1][cur|1]=max(f[i+1][cur|1],f[i][j]+2*x[i+1]);
}
}
}
}
}
for(int j=0;j<1<<k;j++){
if(f[n][j]>ans) ans=f[n][j];
}
cout<<ans-sum;
return 0;
}