【NOIP校内模拟】偷书 grape

---恢复内容开始---

【题目描述】 在 L 的书架上,有 N 本精彩绝伦的书籍,每本书价值不菲。 M 是一个书籍爱好者,他对 L 的书籍早就垂涎三尺。最后他忍受不了诱惑,觉得去偷 L 的 书,为了迅速完成这件事,同时他不希望 L 很快发现书籍少了,他决定偷书时,对于任意 连续的 k 本书,他最多选 B 本,最少选 A 本。现在他想知道怎么选出来的书本最后使得偷 的书籍的价值和,与剩下的书籍价值和,差值最大。

【数据范围】
n<=1000,0<=a<=b<=k<=10 k <= n 所有书籍的价值的绝对值<=10^9

注意到a,b,k<=10,很容易想到状压dp

题目是要求每一段连续的k都满足要求,首先明确,我们选出来的书籍肯定是越大越好。

设dp[i][state]表示前i本书 最后k本书状态为state的最大值

状态转移分为两种情况

1.如果当前状态s最后一位是1 也就是要买最后一本书
dp[i][s]=max(dp[i-1][s>>1]+val[i],dp[i-1][s>>1|(1<<(k-1))]+val[i])

2.如果最后一位是0 即不买
dp[i][s]=max(dp[i-1][s>>1],dp[i-1][s>>1|(1<<(k-1))])

注意还要check一下s>>1 s>>1|(1<<(k-1))两个状态是否合法

参考代码

//dp[i][s]	前i个人 k个人状态为s的最优解 
#include<bits/stdc++.h>
#define N 1005
#define ll long long
using namespace std;
int n,k,a,b,maxs;
ll sum;
ll dp[N][1025];
int nums[1024],val[N];
void init()
{
	for(int s=0;s<1024;s++)
	{
		int temp=s,num=0;
		while(temp)
		{
			if(temp&1)	num++;
			temp>>=1;
		}
		nums[s]=num;
	}
}
bool check(int state)
{
	return (nums[state]>=a&&nums[state]<=b);
}
int main()
{
	cin>>n>>k>>a>>b;
	for(int i=1;i<=n;i++){
		cin>>val[i];
		sum+=val[i]; 
	}
	init();
	for(int i=0;i<(1<<k);i++)	//前k个数 
	{
		for(int j=0;j<k;j++)
		
			if(i&(1<<j))	dp[k][i]+=val[k-j];	//初始化前k个数 
		}
	}
	for(int i=k+1;i<=n;i++)
	{
		for(int s=0;s<(1<<k);s++)
		{
			if(!check(s))	continue;
			int t1=s>>1;
			if(s&1)//要选书 
			{
				int t2=t1|(1<<(k-1));
				if(check(t2))	dp[i][s]=dp[i-1][t2]+val[i];
				if(check(t1))	dp[i][s]=max(dp[i][s],dp[i-1][t1]+val[i]); 
			} 
			else
			{
				int t2=t1|(1<<(k-1));
				if(check(t2))	dp[i][s]=dp[i-1][t2];
				if(check(t1))	dp[i][s]=max(dp[i][s],dp[i-1][t1]);
			}
		}	
	}
	ll ans=LLONG_MIN;
	for(int s=0;s<(1<<k);s++)
	{
		if(check(s))	ans=max(ans,dp[n][s]);
	}
	cout<<ans-sum+ans;
	return 0;
}
posted @ 2018-10-01 22:31  Patrickpwq  阅读(76)  评论(0编辑  收藏  举报