位运算 + 压状dp

应用: 遇到n=10||15||20 这种小数据,就一定要反应到压状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 回车

 

posted @ 2021-11-28 18:17  VxiaohuanV  阅读(79)  评论(0编辑  收藏  举报