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
题解
很神奇的一道题
首先观察魔力的大小,每个魔力大小不超过100000,那么最大的魔力值一定不会超过2^17次方。
那么先寻找每个数能贡献值的位置,假如贡献的位置多于n-k的话,那么可以直接输出了
不行的话,n-k就只可能小于17了,也就是n<117
那么采取dp做
设f[i][j]为前i个数,或的和为j,最多需要删除多少个数
dp方程看代码吧
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
using namespace std;
const int MAXN=(1<<17);
int a[MAXN],n,k,cnt;
bool wz[20];//wz[i]=true表示i位置可以|一个1
int f[120][MAXN];//只需处理n<117的情况
int main()
{
memset(wz,false,sizeof(wz));
cnt=0;
scanf("%d%d",&n,&k);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
for(int j=0;j<17;j++)if((a[i]&(1<<j))>0)wz[j]=true;
}
int ans=0;
for(int i=0;i<17;i++)if(wz[i]==true){cnt++;ans|=(1<<i);}
if(n-k>=cnt){printf("%d\n",ans);return 0;}
for(int i=0;i<=n;i++)for(int j=0;j<MAXN;j++)f[i][j]=-n;
f[0][0]=0;
for(int i=1;i<=n;i++)
{
for(int j=0;j<MAXN;j++)
{
f[i][j]=max(f[i][j],f[i-1][j]+1);//a[i]不参与| 那么删除的数就要+1
f[i][j|a[i]]=max(f[i][j|a[i]],f[i-1][j]);//a[i]参与| 删除的数就不动
}
}
for(int i=1;i<MAXN;i++)if(f[n][i]>=k)ans=i;
printf("%d\n",ans);
return 0;
}

探讨如何通过选择宝石以最大化法杖的威力。面对有限的宝石镶嵌栏,需要丢弃部分宝石,利用动态规划算法求解最优解,确保剩余宝石的魔力值通过按位或运算达到最大。
753

被折叠的 条评论
为什么被折叠?



