【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);
}
bzoj4976

 

posted @ 2018-12-06 19:53  QuartZ_Z  阅读(185)  评论(0编辑  收藏  举报