【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

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;
}
posted @ 2017-08-22 14:32  CQzhangyu  阅读(420)  评论(0编辑  收藏  举报