【BZOJ4976】宝石镶嵌
Description
魔法师小Q拥有n个宝石,每个宝石的魔力依次为w_1,w_2,...,w_n。他想把这些宝石镶嵌到自己的法杖上,来提升法杖的威力。不幸的是,小Q的法杖上宝石镶嵌栏太少了,他必须扔掉k个宝石才能将剩下的宝石镶嵌上去。法杖的威力等于镶嵌在上面的所有宝石的魔力按位做或(OR)运算的结果,请写一个程序帮助小Q做出最佳的选择,使得法杖的威力最大。
Input
第一行包含两个正整数n,k(2<=n<=100000,1<=k<=100,k<n),分别表示宝石的个数以及要扔掉的宝石个数。
第二行包含n个整数w_1,w_2,...,w_n(0<=w_i<=100000),分别表示每个宝石的魔力。
Output
输出一行一个整数,即最大的威力。
Sample Input
4 1
32 16 8 7
32 16 8 7
Sample Output
56
题解:考虑如果n-k>=17,那么我们对于每一位都选一个对应位为1的宝石即可。所以我们只需要考虑n-k<17的情况,即n<117。
那么直接背包DP即可,f[i]表示或=i时所需要的最少的数。
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
const int maxn=100010;
int n,k,ans;
int v[maxn];
int f[maxn<<2];
inline int rd()
{
int ret=0,f=1; char gc=getchar();
while(gc<'0'||gc>'9') {if(gc=='-') f=-f; gc=getchar();}
while(gc>='0'&&gc<='9') ret=ret*10+gc-'0',gc=getchar();
return ret*f;
}
int main()
{
n=rd(),k=rd();
int i,j;
for(i=1;i<=n;i++) v[i]=rd();
if(n-k>=16)
{
for(i=1<<16;i;i>>=1)
{
for(j=1;j<=n;j++) if(v[j]&i) break;
if(j<=n) ans|=i;
}
printf("%d",ans);
return 0;
}
memset(f,0x3f,sizeof(f));
f[0]=0;
for(i=1;i<=n;i++)
{
for(j=(1<<17)-1;~j;j--)
{
f[j|v[i]]=min(f[j|v[i]],f[j]+1);
}
}
for(i=(1<<17)-1;~i;i--)
{
for(j=1<<16;j;j>>=1) if(i&j) f[i^j]=min(f[i^j],f[i]);
}
for(i=1<<16;i;i>>=1)
{
if(f[ans|i]<=n-k) ans|=i;
}
printf("%d",ans);
return 0;
}