这道题需要一点贪心,我们要找到每个书架价值&起来的最大值。从此出发可以想到最大值在二进制意义下如果高位可以取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 }