1. 程式人生 > >【2018/10/01】T2

【2018/10/01】T2

偷書  在L的書架上,有 N本精彩絕倫的書籍,每本書價值不菲。 M 是一個書籍愛好者,他對 L 的書籍早就垂涎三尺。最後他忍受不了誘惑,覺得去偷 L 的書,為了迅速完成這件事,同時他不希望 L 很快發現書籍少了,他決定偷書時,對於任意連續的 k 本書,他最多選 B 本,最少選 A 本。現在他想知道怎麼選出來的書本最後使得偷的書籍的價值和,與剩下的書籍價值和,差值最大。 【 Input】  第一行四個整數 n,k,a,b  一行 N 個整數表示每本書的價值 【 Output】  一個整數表示答案 【 Sample Input】  2 1 0 1  2 -2 【 Sample Output】  4  【 Hint】  得到第一本書 得到的價值和是 2  剩餘的價值和是-2  差值為 4  【資料規模】  對於 20%:n<=10  對於另外 20%:a=0,b=k  對於 100%:n<=1000,0<=a<=b<=k<=10 k<=n 所有書籍的價值的絕對值<=10^9   

分析

好難過……ldw老師欺騙了我的感情,我問他這句話“ 對於 100%:n<=1000,0<=a<=b<=k<=10 k<=n 所有書籍的價值的絕對值<=10^9 ”中的k<=10 k<=n 是什麼鬼,他告訴我就是 k <= n ,然後。。。。。。就完全沒想狀壓了

實際上就是 k<=10 

先來轉化一下:偷的書籍的價值和,與剩下的書籍價值和,差值最大,也就是說 ans = 2*maxn - sum,由於sum是所有書的價值和,是一個定值,要 ans 最大也就是要 maxn 最大

那麼我們定義 f [ i ] [ s ] 表示當前遍歷到第 i 個點,包括 i 在內的 k 本書的狀態在 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;
}