BZOJ4976:宝石镶嵌(DP&思维)
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
思路:之前遇到一个类似的题,不过是XOR不是OR,此题由于是OR,当要留的数比较多的时候一定能取到最大值,即a1|a2|...|an。
否则,我们可以dp,用dp[i][j]表示删去i个能否OR得到j。不难得到下面代码,复杂度O(17*N*1<<17),T了。
#include<bits/stdc++.h> using namespace std; const int maxn=132010; int dp[17][maxn],a[maxn],ans,Mx; int main() { int N,K; scanf("%d%d",&N,&K); K=N-K; for(int i=1;i<=N;i++){ scanf("%d",&a[i]); Mx|=a[i]; } if(K>=17) printf("%d\n",Mx); else { dp[0][0]=1; for(int i=1;i<=N;i++){ for(int k=1;k<=K;k++) for(int j=0;j<=Mx;j++){ dp[k][j|a[i]]|=dp[k-1][j]; } } for(int i=1;i<=Mx;i++) if(dp[K][i]) ans=i; printf("%d\n",ans); } return 0; }
换个DP,我们用dp[i][j]表示前面i个得到j最多删去多少个,最后dp[N][i]>=K的最大i即是答案。
#include<bits/stdc++.h> using namespace std; const int maxn=1<<17; int dp[120][maxn],a[maxn],ans,Mx; //dp:最多可以删去 int main() { int N,K; scanf("%d%d",&N,&K); for(int i=1;i<=N;i++){ scanf("%d",&a[i]); Mx|=a[i]; } if(N-K>=16) printf("%d\n",Mx); else { for(int i=0;i<=N;i++) for(int j=0;j<=Mx;j++) dp[i][j]=-1000000; dp[0][0]=0; for(int i=1;i<=N;i++){ for(int j=0;j<=Mx;j++){ dp[i][j]=max(dp[i][j],dp[i-1][j]+1); dp[i][j|a[i]]=max(dp[i][j|a[i]],dp[i-1][j]); } } for(int i=Mx;i>=0;i--) if(dp[N][i]>=K){ ans=i; break; } printf("%d\n",ans); } return 0; }
It is your time to fight!