动态规划之状压dp

这篇具有很好参考价值的文章主要介绍了动态规划之状压dp。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

  相信在学完动态规划之后,就会接触到动态规划的许多进阶的版本,这里我将简单讲讲状压dp,顾名思义,状压,为什么叫状压~就是状态压缩,就是一种优化动态规划算法的技术,通常用于处理状态空间较大的问题。在状压动态规划中,我们会使用较小的数据结构来表示状态,并利用位运算来进行状态转移和计算,从而减少内存消耗和提高计算效率。具体来说,状压动态规划通常会使用整数来表示状态,其中整数的每一位对应于问题中的某种特定情况或特征。通过位运算,我们可以高效地表示和操作大量的状态,并且可以利用位运算的性质来进行状态转移和计算,从而减少时间和空间复杂度。状压动态规划通常用于处理状态空间较大的问题,例如组合优化问题、子集相关问题等。通过使用状压动态规划,我们可以更高效地解决这些问题,并且可以在一定程度上降低算法的时间复杂度和空间复杂度。

  总的来说,状压动态规划是一种优化动态规划算法的技术,通过使用整数来表示状态并利用位运算来进行状态转移和计算,以提高算法的效率和性能。

  在介绍完状压dp的概念之后,现在咱来回顾几个用得上的语法内容,因为在许多数据结构之中是用的比较少的。首先来回顾"<<"这是个什么?嗯,在C++中,<< 是位移运算符,用于将一个数的所有位向左移动指定的位数。在计算机编程中,左移运算符 << 不是表示数学上的指数运算,而是表示对数字进行位移操作。具体来说,对于整数 n,n << m 表示将 n 的二进制表示向左移动 m 位,相当于将 n 乘以 2 的 m 次幂。因此,1 << 20 表示将数字 1 左移 20 位,相当于将 1 乘以 2 的 20 次幂。这并不等同于数学上的指数运算,而是位运算的结果在这种情况下,1<<20 表示将整数1向左移动20位,相当于计算2的20次方,结果为1048576。表达式 2 << 10 表示将数字 2 左移 10 位。在计算机中,左移运算符 << 会将一个数的二进制表示向左移动指定的位数,相当于将这个数乘以 2 的指定次幂。因此,2 << 10 的结果是 2 乘以 2 的 10 次幂,即 2 的 10 次方,结果为 1024。

  OK,<<相信大家了解差不多了吧,那现在就介绍"|":

在C++中,竖线(|)运算符代表按位或运算。它用于对两个整数的每个对应位执行逻辑或操作,返回的结果是两个数对应位上的逻辑或结果。例如,对于两个二进制数a和b,a | b将返回一个新的二进制数,其中每个位上的值是a和b对应位上值的逻辑或结果。

例如:

a = 5; // 二进制表示为 0101

b = 3; // 二进制表示为 0011

c = a | b; // c的值为 7,二进制表示为 0111

在上面的示例中,a | b的结果是7,因为在二进制表示中,对应的位上进行了逻辑或操作。

  上文是一些基础知识,那回顾一下memset一个stl容器用于将一块内存空间设置为指定的值:一般的用法void *memset(void *ptr, int value, size_t num);

  • ptr 是指向要设置值的内存起始地址的指针。
  • value 是要设置的值,通常是一个无符号字符。
  • num 是要设置的字节数。

  这些东西回顾完了,其实在面对状压dp的时候的步骤和动态规划的差不多,只不过要多进行一个'|'操作进行状态压缩。干说是不是有点不太好理解下面上题目:

题目描述

糖果店的老板一共有 M 种口味的糖果出售。为了方便描述,我们将 M 种 口味编号 1 ∼ M。

小明希望能品尝到所有口味的糖果。遗憾的是老板并不单独出售糖果,而 是 K 颗一包整包出售。

幸好糖果包装上注明了其中 K 颗糖果的口味,所以小明可以在买之前就知 道每包内的糖果口味。

给定 N 包糖果,请你计算小明最少买几包,就可以品尝到所有口味的糖 果。

输入格式

第一行包含三个整数 N、M 和 K。

接下来 N 行每行 K 这整数 T1, T2, · · · , TK,代表一包糖果的口味

(对于 30% 的评测用例,1 ≤ N ≤ 20 。

对于所有评测样例,1 ≤ N ≤ 100,1 ≤ M ≤ 20,1 ≤ K ≤ 20,1 ≤ Ti ≤ M 。)

输出格式

一个整数表示答案。如果小明无法品尝所有口味,输出 −1。

样例输入

复制

6 5 3
1 1 2
1 2 3
1 1 3
2 3 5
5 4 2
5 1 2

样例输出

复制

2

分析完题目,发现数据还挺好的咱可以用 n位二进制数来表示所购买糖果中 n 种口味的组合情况。

其中有1是不是就是代表有这个糖果,0就是没有?好好理解一下。

  那咱怎样确定dp呢?嗯~既然要用01这种二进制来表示全部情况,我们可以用dp[i]:表示状态为i时所需的最小糖果包数。状态i表示口味的组合情况,每个口味对应状态中的一位。dp数组记录了每种口味组合所需的最小糖果包数,通俗一点讲用一个二进制数来表示口味的组合情况。比如,如果口味有4种,那么0001表示只包含口味1,1010表示包含口味2和口味4。dp[i]表示当口味组合为i时,所需的最小糖果包数。也就是说,dp数组记录了每种口味组合所需的最小糖果包数。

  OK代码如下

#include <iostream>
#include <cstring>
using namespace std;

const int MAX_N = 20;

int main() {
    int n, m, k;
    int dp[1 << MAX_N]; // 使用状压的数组需要开2^n,因为表示的是状态
    int v[MAX_N]; // 记录每种糖果的口味状态

    // 读入口味种类数n、糖果总种类数m、每种糖果的口味数k
    cin >> n >> m >> k;

    // 初始化dp数组为一个较大的数,表示无穷大
    memset(dp, 0x3f, sizeof(dp));

    // 读入每种糖果的口味状态
    for (int i = 0; i < n; ++i) {
        int flavors = 0;
        for (int j = 0; j < k; ++j) {
            int flavor;
            cin >> flavor;
            flavor--; // 口味从1开始,数组从0开始,需要减1
            flavors |= (1 << flavor); // 使用按位或操作记录口味状态
        }
        dp[flavors] = 1; // 这些口味都可以用一包糖解决
        v[i] = flavors; // 记录糖的状态
    }

    // 状态转移,枚举所有可能的状态
    for (int i = 0; i < (1 << m); ++i) {
        for (int j = 0; j < n; ++j) {
            dp[i | v[j]] = min(dp[i | v[j]], dp[i] + 1); // 更新dp数组的值
        }
    }

    // 输出结果
    if (dp[(1 << m) - 1] == 0x3f3f3f3f) {
        cout << -1; // 无法搭配出来
    } else {
        cout << dp[(1 << m) - 1]; // 搭配出来所需的最少糖果包数量
    }

    return 0;
}

这样就通过一个状压dp解决啦这个题目,由于本人水平有限,有错误指出还恳请各位指出^_^文章来源地址https://www.toymoban.com/news/detail-853859.html

到了这里,关于动态规划之状压dp的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 算法——动态规划(DP)——递推

    动态规划常用于解决优化问题。 动态规划通常以自底向上或自顶向下的方式进行求解。 自底向上的动态规划从最简单的子问题开始,逐步解决更复杂的问题,直到达到原始问题。 自顶向下的动态规划则从原始问题出发,分解成子问题,并逐步求解这些子问题。 动态规划算法

    2024年01月20日
    浏览(46)
  • 动态规划(DP)(算法笔记)

    本文内容基于《算法笔记》和官方配套练题网站“晴问算法”,是我作为小白的学习记录,如有错误还请体谅,可以留下您的宝贵意见,不胜感激。 动态规划(Dynamic Programming,DP)是一种用来解决一类最优化问题的算法思想。简单来说,动态规划将一个复杂的问题分解成若干个子

    2024年02月05日
    浏览(38)
  • 【算法】动态规划(dp问题),持续更新

    介绍本篇之前,我想先用人话叙述一般解决动态规划问题的思路: 动态规划的问题,本身有许多产生结果的可能,需要在具体题目下得到满足某个条件的解。 如何得到呢? 我们就需要根据这个具体问题,建立一个状态表( dp 表 ),在这张 dp 表中的每一个位置的数据都有明

    2024年02月04日
    浏览(40)
  • C++动态规划-线性dp算法

    莫愁千里路 自有到来风 CSDN 请求进入专栏                                    X 是否进入《 C++ 专栏》? 确定 目录  线性dp简介 斐波那契数列模型  第N个泰波那契数 思路: 代码测试:  三步问题 思路: 代码测试: 最小花费爬楼梯 思路: 代码测试:  路径问题 数字三

    2024年02月19日
    浏览(36)
  • 算法——动态规划(DP,Dynamic Programming)

    动态规划常用于解决优化问题。 动态规划通常以自底向上或自顶向下的方式进行求解。 自底向上的动态规划从最简单的子问题开始,逐步解决更复杂的问题,直到达到原始问题。 自顶向下的动态规划则从原始问题出发,分解成子问题,并逐步求解这些子问题。 动态规划算法

    2024年02月02日
    浏览(46)
  • 算法套路十三——动态规划DP入门

    动态规划和递归都是通过将大问题分解为较小的子问题来解决问题。它们都可以用来解决具有重叠子问题和最优子结构特性的问题。 递归是一种自顶向下的方法, 它从原始问题开始 ,递归地将问题分解为较小的子问题dfs(i)—— dfs(i)代表的是从第i个状态开始进行递归求解能

    2024年02月15日
    浏览(46)
  • c++ 算法之动态规划—— dp 详解

    dp 是 c++ 之中一个简单而重要的算法,每一个 OIer 必备的基础算法,你知道它究竟是什么吗? 目录 一、简介         1.为什么不能贪心?         2.揭秘 dp   二、应用         1.定义         2.边界值         3.动态转移方程         4.结果         5.代码

    2024年04月09日
    浏览(40)
  • acwing算法基础之动态规划--线性DP和区间DP

    线性DP:状态转移表达式存在明显的线性关系。 区间DP:与顺序有关,状态与区间有关。 题目1 :数字三角形。 解题思路:直接DP即可, f[i][j] 可以来自 f[i-1][j] + a[i][j] 和 f[i-1][j-1] + a[i][j] ,注意 f[i-1][j] 不存在的情况(最后一个点)和 f[i-1][j-1] 不存在的情况(第一个点)。

    2024年02月04日
    浏览(40)
  • Day36算法记录|动态规划 dp02

    步骤回顾: C语言版本写的很清楚 对应得Java版本视频解析 方法一: 动态规划 1 确定dp数组(dp table)以及下标的含义 dp[i][j] :表示从(0 ,0)出发,到(i, j) 有dp[i][j]条不同的路径。 2 . 确定递推公式 ,求dp[i][j],只能有两个方向来推导出来,即dp[i - 1][j] 和 dp[i][j - 1]。 3. dp数

    2024年02月12日
    浏览(38)
  • C++算法 —— 动态规划(7)两个数组的dp

    每一种算法都最好看完第一篇再去找要看的博客,因为这样会帮你梳理好思路,看接下来的博客也就更轻松了。当然,我也会尽量在写每一篇时都可以不懂这个算法的人也能边看边理解。 动规的思路有五个步骤,且最好画图来理解细节,不要怕麻烦。当你开始画图,仔细阅读

    2024年02月07日
    浏览(43)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包