【题目】
题目描述:
在 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,所有书籍的价值的绝对值 ≤
【分析】
看到 K ≤ 10 这么小的范围就很容易想到状压了
用 f[ i ][ s ] 表示前 i 个人状态为 s 的答案
转移就从上一位(i - 1)位转就可以了
【代码】
#include<bits/stdc++.h>
#define ll long long
#define N 1009
#define in read()
using namespace std;
ll val[N],f[N][1025];
int n,k,a,b;
bool leg[1025];
inline int read(){
char ch;int res=0;
while((ch=getchar())<'0'||ch>'9');
while(ch>='0'&&ch<='9'){
res=(res<<3)+(res<<1)+ch-'0';
ch=getchar();
}
return res;
}
void init(){
int status=(1<<10);
for(int i=0;i<status;++i){
int num=0,h=i;
while(h){
if(h&1) num++;
h>>=1;
}
if(num>=a&&num<=b) leg[i]=1;
}
}
int main(){
n=in;k=in;a=in;b=in;
init(); int maxn=(1<<k);
ll sum=0;
for(int i=1;i<=n;++i) {scanf("%lld",&val[i]);sum+=val[i];}
memset(f,-1,sizeof(f));
for(int i=0;i<maxn;++i) f[0][i]=0;
for(int i=1;i<=n;++i){
for(int s=0;s<maxn;++s){
if(leg[s]){
if(f[i-1][(s>>1)]!=-1){
if(s&1) f[i][s]=max(f[i][s],f[i-1][s>>1]+val[i]);
else f[i][s]=max(f[i][s],f[i-1][s>>1]);
}
if(f[i-1][(s>>1)|(1<<k-1)]!=-1){
if(s&1) f[i][s]=max(f[i][s],f[i-1][(s>>1)|(1<<k-1)]+val[i]);
else f[i][s]=max(f[i][s],f[i-1][(s>>1)|(1<<k-1)]);
}
}
}
}
ll ans=0;
for(int i=0;i<maxn;++i){
if(f[n][i]==-1) continue;
ans=max(ans,f[n][i]);
}
printf("%lld",2*ans-sum);
return 0;
}