Codeforces 981D Bookshelves 【dp】【性质】

这道题需要一点贪心,我们要找到每个书架价值&起来的最大值。从此出发可以想到最大值在二进制意义下如果高位可以取1,那么高位取1的数一定比高位不取1的数要大。如10000000大于01111111

因此不难想到我们从最高位往下枚举每一位就可以了,那么我们现在要解决的问题是给一个数x,让我们判断n本书放在k个书架里能不能找到一个放的方式使得每个书架价值&起来是x

由此想到用dp,我们dp[i][j],代表前i本书放进前j个书架里&价值能不能等于x(注意到实现的时候要在递归函数里带个参数x)。那转移方程怎么写呢?

我们枚举k (k从j-1到i-1) ,如果dp(k,j-1,x) && ((sum[i]-sum[k])&x)==x,那dp[i][j]=1

本质上我们枚举最后一个书架上放书的数量。比如我们遇到一个问题7本书放在5个书架里能不能&起来是x,那就是看( 4本书放在4个书架里,第五个书架放第5,6,7本书),( 5本书放在4个书架里,第五个书架放第6,7本书),( 6本书放在4个书架里,第五个书架放第7本书)这三个方案有没有任何一个方案使得书架&起来是x,如果有的话那returnn dp[7][5]=1

最后考虑边界情况,如果j=1,那么 if( (sum[i]&x) ==x) return dp[i][j]=1 不然是0

这里解释下sum[i]&x==x的意思,他指的是第一个书架放前i本书,判断书架价值&x等不等于x,如果等于x的话能就是合理的放书方式。

 

 1 #include<iostream>
 2 #include<cstring>
 3 using namespace std;
 4 
 5 long long sum[55],ans;
 6 int dp[55][55];//前i个书放进k个书架里&起来能不能等于check里的x
 7 
 8 bool check(int i,int j,long long x){
 9     if(dp[i][j]!=-1) return dp[i][j];
10     if(j==1){
11         if( (sum[i]&x) ==x) return dp[i][j]=1;
12         return dp[i][j]=0;
13     }
14     for(int k=j-1;k<i;k++){
15         if( check(k,j-1,x) && ((sum[i]-sum[k])&x)==x ) return dp[i][j]=1;
16     }
17     return dp[i][j]=0;
18 } 
19 
20 int main(){
21     int n,k; cin>>n>>k;
22     for(int i=1;i<=n;i++) { cin>>sum[i]; sum[i]+=sum[i-1]; }
23     
24     for(int i=60;i>=0;i--){//枚举ans在二进制下的每一位
25         memset(dp,-1,sizeof(dp));
26         long long a=1; a=a<<i;
27         if( check(n,k,ans+a) ) ans+=a;
28     }
29     cout<<ans<<endl;
30     
31     return 0;
32 }

 

posted @ 2018-05-30 11:06  4397  阅读(268)  评论(0编辑  收藏  举报