dp状态压缩

用状态压缩表示糖果口味

大佬:我们简单演示一下用状态压缩表示一包糖果中的口味。例如输入一包糖果的“2,3,5”三种口味。要把这 3 个口味压缩成二进制数 10110 ,就需要做“移位”和“或”操作:

定义初始值 tmp = 0
输入口味“2”。先做移位 1<<(2−1),得二进制数 10;然后再与 tmp或,得 tmp= tmp| 10 = 10
输入口味“3”。先做移位 1<<(3−1),得二进制数 100;然后再与 tmp 或,得 tmp= tmp| 100 = 110
输入口味“5”。先做移位1<<(5−1),得二进制数10000;然后再与 tmp或,得 tmp= tmp| 100 = 10110

|的用法

|: 二进制“或”(有1时,结果是1,都是0时,结果为0。),比如:1010 | 1011 = 1011,1010 | 1000 = 1010。 两个数 只要有1就是1

#include<bits/stdc++.h>
using namespace std;
int dp[1<<20];  //dp[v]得到口味为V时所需要的糖果数量
int kw[100]; //第i包糖果的口味 存放状态压缩后数字的数组
int main(){
    int n,m,k;
    cin >> n >> m >> k ; //共n行 (n包) 每行k个数(一包K颗) 总共有m种口味
    int tot = (1<<m) - 1;
    memset(dp,-1,sizeof dp);
    for(int i = 0;i < n;i++){
        int tmp = 0;
        for(int j = 0; j < k;j++){ 
            //遍历每行每个数
            int x;
            cin>>x;
            tmp = tmp | (1<<x-1);
        }
        kw[i] = tmp ;
        dp[tmp] = 1; //得到这个压缩状态的数字 需要 1包
    }
    for(int i = 0;i<=tot;i++){
        if(dp[i]!=-1) 
        //是之前录入的状态 
        {
            for(int j = 0;j<n;j++){
                int tmp = kw[j]; // 把状态压缩后的数字给tmp
                if(dp[i|tmp] == -1 || dp[i|tmp] > dp[i]+1) //出现了新的组合 || 这个新组合之前的值过大/不是最少的答案
                  dp[i|tmp] = dp[i]+1;
            }
        }
    }
    cout << dp[tot];
    return 0;
}

状态压缩的原理

状态压缩 DP 的应用背景是以集合为状态,且集合一般用二进制来表示,用二进制的位运算来处理。

集合问题一般是指数复杂度的,例如:

1.子集问题,设元素无先后关系,那么共有 2^n个子集。
2.排列问题,对所有元素进行全排列,共有 n!n! 个全排列。

位运算

#include<bits/stdc++.h>
int main(){
    int a = 213, b = 21;            //a = 1101 0101 , b= 0001 1001
    printf("a & b = %d\n",a & b);   // AND  =  17, 二进制0001 0001
    printf("a | b = %d\n",a | b);   // OR   = 221, 二进制1101 1101
    printf("a ^ b = %d\n",a ^ b);   // XOR  = 204, 二进制1100 1100
    printf("a << 2 = %d\n",a << 2); // a*4  = 852, 二进制0011 0101 0100
    printf("a >> 2 = %d\n",a >> 2); // a/4  =  53, 二进制0011 0101

    int i = 5;                      //(1)a的第i位是否为1
    if((1 << (i-1)) & a)  printf("a[%d]=%d\n",i,1);  //a的第i位是1 
    else                  printf("a[%d]=%d\n",i,0);  //a的第i位是0

    a = 43, i = 5;                  //(2)把a的第i位改成1。a = 0010 1011
    printf("a=%d\n",a | (1<<(i-1))); //a=59, 二进制0011 1011
    
    a = 242;                        //(3)把a最后的1去掉。  a = 1111 0010
    printf("a=%d\n", a & (a-1));     //去掉最后的1。   =240, 二进制1111 0000

    return 0;    
}

请添加图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值