【BZOJ4976】宝石镶嵌 DP
【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; }
| 欢迎来原网站坐坐! >原文链接<