【题目描述】
在L的书架上,有 N本精彩绝伦的书籍,每本书价值不菲。
M 是一个书籍爱好者,他对 L 的书籍早就垂涎三尺。最后他忍受不了诱惑,觉得去偷 L 的书,为了迅速完成这件事,同时他不希望 L 很快发现书籍少了,他决定偷书时,对于任意连续的 k 本书,他最多选 B 本,最少选 A 本。现在他想知道怎么选出来的书本最后使得偷的书籍的价值和,与剩下的书籍价值和,差值最大。
【输入格式】
第一行四个整数 n,k,a,b
一行 N 个整数表示每本书的价值
【输出格式】
一个整数表示答案
【样例输入】
2 1 0 1
2 -2
【样例输出】
4
【备注】
对于 20%:n<=10
对于另外 20%:a=0,b=k
对于 100%:n<=1000,0<=a<=b<=k<=10,所有书籍的价值的绝对值<=10^9
【题目分析】
一看到k<=10,这么小的数据简直就是宣告这是一道状压DP的题,那么就定义一个数组dp[i][j],其中i表示前i个,状态为j(包含第i个)的最大价值,易得对于每个合法dp[i][j],均可从dp[i-1][j>>1]和dp[i-1][(j>>1)|(1<<k)]转移而来,所以先预处理出所有合法情况(即1的个数大于等于a而小于等于b),最后扫一遍dp[n][k]即可。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<queue>
#include<cmath>
using namespace std;
typedef long long LL;
const LL MAXN=1<<11;
const LL INF=0x3f3f3f3f;
LL dp[1001][MAXN];
LL n,k,a,b;
LL val[1001];
bool che[MAXN];
LL Read()
{
LL i=0,f=1;
char c;
for(c=getchar();(c>'9'||c<'0')&&c!='-';c=getchar());
if(c=='-')
f=-1,c=getchar();
for(;c>='0'&&c<='9';c=getchar())
i=(i<<1)+(i<<3)+c-'0';
return i*f;
}
void pre()
{
for(LL i=0;i<=MAXN;++i)
{
LL t=i;
LL cnt=0;
while(t)
{
if(t&1)
cnt++;
t>>=1;
}
if(cnt>=a&&cnt<=b)
che[i]=true;
}
}
int main()
{
memset(dp,INF,sizeof(dp));
n=Read(),k=Read(),a=Read(),b=Read();
LL ans=-dp[0][0],inf=dp[0][0];
pre();
LL tot=0;
for(LL i=1;i<=n;++i)
val[i]=Read(),tot+=val[i];
for(LL j=0;j<(1<<k);++j)
dp[0][j]=0;
LL cs=1<<k;
for(LL i=1;i<=n;++i)
{
for(LL j=0;j<cs;++j)
{
if(che[j])
{
if(dp[i-1][j>>1]!=inf&&dp[i-1][(j>>1)|(1<<(k-1))]!=inf)
{
if(j&1)
dp[i][j]=max(dp[i-1][j>>1],dp[i-1][(j>>1)|(1<<(k-1))])+val[i];
else
dp[i][j]=max(dp[i-1][j>>1],dp[i-1][(j>>1)|(1<<(k-1))]);
}
else
{
dp[i][j]=min(dp[i-1][j>>1],dp[i-1][(j>>1)|(1<<(k-1))]);
if(dp[i][j]==inf)
continue;
if(j&1)
dp[i][j]+=val[i];
}
}
}
}
for(LL i=0;i<cs;++i)
{
if(dp[n][i]==inf)
continue;
ans=max(ans,dp[n][i]);
}
cout<<ans*2-tot;
return 0;
}