【2018/10/01測試T2】【WOJ 2688】偷書
阿新 • • 發佈:2018-12-13
【題目】
題目描述:
在 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; }