• 位运算 + 压状dp


    位运算:(二进制)

    1. ’&’符号,x&y,会将两个十进制数在二进制下进行与运算(都1为1,其余为0) 然后返回其十进制下的值。例如3(11)&2(10)=2(10)。
    2. ’|’符号,x|y,会将两个十进制数在二进制下进行或运算(都0为0,其余为1) 然后返回其十进制下的值。例如3(11)|2(10)=3(11)。
    3. ’^’符号,x^y,会将两个十进制数在二进制下进行异或运算(不同为1,其余 为0)然后返回其十进制下的值。例如3(11)^2(10)=1(01)。
    4. ’~’符号,~x,按位取反。例如~101=010。
    5. ’<<’符号,左移操作,x<<2,将x在二进制下的每一位向左移动两位,最右边用0填充,x<<2相当于让x乘以4。 ’>>’符号,是右移操作,x>>1相当于给x/2,去掉x二进制下的最右一位

    1.判断一个数字x二进制下第i位是不是等于1。(最低第1位)

    方法:if(((1<<(i−1))&x)>0) 将1左移i-1位,相当于制造了一个只有第i位 上是1,其他位上都是0的二进制数。然后与x做与运算,如果结果>0, 说明x第i位上是1,反之则是0。

    2.将一个数字x二进制下第i位更改成1。

    方法:x=x|(1<<(i−1)) 证明方法与1类似。

    3.将一个数字x二进制下第i位更改成0。

    方法:x=x&~(1<<(i−1))

    4.把一个数字二进制下最靠右的第一个1去掉。

    方法:x=x&(x−1)

    压状dp:

      把情况 用 二进制 表示 具体的二进制数中 1 代表 有 0 则是没有 一个二进制数就可以表示一种 情况。 很多个二进制数就可以把情况表示完。

      特别注意: 0 的情况也要考虑。

      在利用背包的思想,对这些情况进行叭叭。

    试题 历届真题 糖果【第十届】【省赛】【A组】
         
    资源限制
    时间限制:1.0s   内存限制:256.0MB
    问题描述
      糖果店的老板一共有 M 种口味的糖果出售。为了方便描述,我们将 M 种口味编号 1 ∼ M。
      小明希望能品尝到所有口味的糖果。遗憾的是老板并不单独出售糖果,而是 K 颗一包整包出售。
      幸好糖果包装上注明了其中 K 颗糖果的口味,所以小明可以在买之前就知道每包内的糖果口味。
      给定 N 包糖果,请你计算小明最少买几包,就可以品尝到所有口味的糖
      果。
    输入格式
      第一行包含三个整数 N、M 和 K。
      接下来 N 行每行 K 这整数 T₁, T₂, · · · , TK,代表一包糖果的口味。
    输出格式
      一个整数表示答案。如果小明无法品尝所有口味,输出 −1。
    样例输入
    6 5 3
    1 1 2
    1 2 3
    1 1 3
    2 3 5
    5 4 2
    5 1 2
    样例输出
    2
    评测用例规模与约定
      对于 30% 的评测用例,1 ≤ N ≤ 20 。
      对于所有评测样例,1 ≤ N ≤ 1001 ≤ M ≤ 201 ≤ K ≤ 201 ≤ Ti ≤ M。
    A simply example
    #include <bits/stdc++.h>
    using namespace std;
    #define ri register int
    #define N 10005
    
    template <class G>void read(G &x)
    {
        x=0;int f=0;char ch=getchar();
        while(ch<'0'||ch>'9'){f|=ch=='-';ch=getchar();}
        while(ch<='9'&&ch>='0'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
        x=f?-x:x;
        return ;
    }
    
    const int M = 1<<20+2;
    
    int dp[M];
    
    int n,m,K;
    int candy[105];
    int main(){
        
        read(n);read(m);read(K);
        for(ri i=1;i<=n;i++)
        {
            for(ri j=1;j<=K;j++)
            {
                int a; read(a);
                candy[i]|=1<<(a-1);
            }
            dp[candy[i]]=1;
        }
        for(ri i=1;i<=n;i++)
        {
            for(ri j=1;j<(1<<m);j++)
            {
                if(!dp[j]) continue;   /// 就是装满问题 
                if(dp[j|candy[i]]==0||dp[j|candy[i]]>dp[j]+1)
                dp[j|candy[i]]=dp[j]+1;
            }
        }
        
        if(dp[(1<<m)-1]) printf("%d",dp[(1<<m)-1]);
        else cout<<-1;
        
        return 0;    
    }
    Code

    知识点 : 判断 一个二进制的数 的 1之间是否 有 0隔开 直接 a<<1 &a  看存在是否

                    判断 2 个二进制数的1 是否 错位 不仅要 a<<1 &b && a>>1&b 关键

                    注意0的初始情况;

    注意 是 围着的那 8 个数

    试题 算法提高 鹅卵石
         
    资源限制
    时间限制:1.0s   内存限制:256.0MB
      题意:  给定一个N*N的方格,让你在里面取出一些数使其和最大,要求每一个数不能与其相邻的8个数同时取出。输出最大和即可。
    输入格式
      一个N*N 矩阵
    输出格式
      最大和  样例:  71 24 95 56 54  85 50 74 94 28  92 96 23 71 10  23 61 31 30 46  64 33 32 95 89
    输出格式
      572
    View problem
    #include <bits/stdc++.h>
    using namespace std;
    #define ri register int 
    const int M = 1<<15;
    
    
    int n;
    
    vector <int>q;
    
    void init()
    {
        
        for(ri i=0;i<(1<<n);i++)
        {
            if((i<<1)&i) continue;
            q.push_back(i);
        }
        
    }
    int p[21][21];
    long long ar[16][M];
    void ck(int a,int b)
    {
        for(ri i=1;i<=n;i++)
        {
            if(b&(1<<(i-1)))
            {
                ar[a][b]+=p[a][i];
            }
        }
    }
    
    long long  f[16][M];
    int qu[M];
    int main(){
        int cent=0;
        int aa;
        while(~scanf("%d",&aa))
        {
            qu[++cent]=aa;
        }
        n=sqrt(cent);
        cent=0;
        for(ri i=1;i<=n;i++)
        {
        
        for(ri j=1;j<=n;j++)
        {
            p[i][j]=qu[++cent];
        }
        }
        init();
        for(ri i=1;i<=n;i++)
        {
            for(ri j=0;j<q.size();j++)
            {
                int a=q[j];
                ck(i,a);
    
            }
    
        }
        long long  ans=0;
        for(ri i=0;i<q.size();i++)
        {
            int a=q[i];
            f[1][a]=ar[1][a];
        }
        
        for(ri i=2;i<=n;i++)
        {
            for(ri j=0;j<q.size();j++)
            {
                int a=q[j];
                for(ri k=0;k<q.size();k++)
                {
                    int b=q[k];
                    if(((b<<1)&a)) continue;
                    if((b>>1)&a) continue;
                    if(b&a) continue;
                    f[i][a]=max(f[i][a],f[i-1][b]+ar[i][a]);
                }
    
            }
            
        }
        for(ri i=0;i<q.size();i++)
        {
            int b=q[i];
            ans=max(ans,f[n][b]);
        }
        printf("%lld",ans);
        
        
        return 0;
    }
    View Code

    小知识点: 看看如何输入的; 查看时候 正确 就 先回车 在 ctrl + z 回车

  • 相关阅读:
    【剑指offer】对称的二叉树
    【剑指offer】数组中的逆序对
    【剑指offer】不用加减乘除做加法
    【剑指offer】和为S的连续正数序列
    【剑指offer】删除链表中重复的结点
    【剑指offer】平衡二叉树
    Math.ceil()、Math.floor()和Math.round()
    document.querySelectorAll遍历
    JS选择器querySelector和~All,三个原生选择器
    js 操作select和option常见用法
  • 原文地址:https://www.cnblogs.com/Lamboofhome/p/15616053.html
Copyright © 2020-2023  润新知