偷书[状压DP]

题目描述:

在 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,所有书籍的价值的绝对值 ≤ 10^{9}


自己写状压写对了耶,好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;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

FSYo

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值