【bzoj4976】宝石镶嵌(思维dp)
题目传送门:bzoj4976
不得不说这是道脑洞dp,思路真的清奇。
我们可以发现,虽然n很大,但是k只有100,这里面似乎隐藏了什么玄机。
我们可以发现,设总共有$ tot $个二进制位在这n个数中有出现过1,那么当$ n-k>=tot $时,所有二进制位都能取到,可以直接计算答案。
当$ n-k<tot $时,n最大只有116(因为宝石价值最大只有1e5<2^17),那么就可以随便dp一下:设$ f[i][j] $表示处理前$ i $个数,当前或运算结果为$ j $时,最多能选取多少个数,那么就可以得到:$ f[i][j|w[i]]=max(f[i][j|w[i]],f[i-1][j]+1) $
代码:
#include<cstdio> #include<algorithm> #include<queue> #define maxn 100010 int a[maxn],f[180010]; int n,k; inline char nc(){ static char buf[100000],*p1=buf,*p2=buf; return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++; } inline void read(int &x) { x=0; char c=nc(); for(;c<'0'||'9'<c;c=nc()); for(;'0'<=c&&c<='9';c=nc())x=(x<<3)+(x<<1)+c-'0'; } int main() { read(n); read(k); int mx=0; for(int i=1;i<=n;i++){ read(a[i]); if(a[i]>mx)mx=a[i]; } int tot=0,tmp=0,hhh=0; for(int i=1;i<=mx;i<<=1,++hhh){ int flag=0; for(int j=1;j<=n;j++) if(a[j]&i){ flag=1; break; } if(flag)++tot,tmp|=i; } if(n-k>=tot){ printf("%d\n",tmp); return 0; } for(int i=0;i<(1<<hhh);i++)f[i]=0x3f3f3f3f; f[0]=0; for(int i=1;i<=n;i++) for(int j=(1<<hhh)-1;j>=0;j--) if(f[j]+1<f[j|a[i]])f[j|a[i]]=f[j]+1; int ans=0; for(int i=0;i<(1<<hhh);i++) if(f[i]+k<=n&&ans<i)ans=i; printf("%d\n",ans); }