首页 > 其他分享 >关于动态规划的一些理解

关于动态规划的一些理解

时间:2024-08-10 17:17:27浏览次数:9  
标签:01 max 理解 背包 物品 动态 规划 DP

关于动态规划的一些理解

1. 什么是动态规划

动态规划(DP,Dynamic Programming)是一种解决问题的方法。它通过将难以实现的整体的大问题划分成简单的局部的小问题。最后将小问题一一求解以完成问题。

对于动态规划能否使用有一些限制,这些限制我推荐参看动态规划基础 - OI Wiki中的描述。(实际上是不是用DP看一眼题就知道了)。

动态规划只是一种解决问题的方法,它并不是类似于DFS(深度优先搜索)有特定实现的算法,具体问题具体分析。

2. 常见的动态规划类型以及理解

2.1 背包DP

背包问题是动态规划的一个分支。对于大部分人而言,背包DP更容易理解、入门动态规划。

背包问题本质是在多个种类物品之间做选择。由于[重量]等原因对于选择做了限制,通常求选择的最优解。

2.1.1 01背包

01背包是背包问题的一种。该类问题需要在多个种类的物品之间做出选择,每个种类只能选择一个,由于“选与不选”的特性,类似于二进制中的01[真假],所以称为01背包。

下面对于01背包一个例题:

P2871 [USACO07DEC] Charm Bracelet S - 洛谷

题目大意:

N 件物品和一个容量为 M 的背包。第 i 件物品的重量是 Wi,价值是 Di。求解将哪些物品装入背包可使这些物品的重量总和不超过背包容量,且价值总和最大。

输入样例:

4 6
1 4
2 6
3 12
2 7

输出样例:

23

解题思路:

这是一道01背包问题的模板题。对于容量 M 的背包,装 x 个物品使其总价值 D 最大。最先想到的可能是贪心。但是由于重量的存在,使得价值最高可能不是收益最高。高价值高重量的物品可能不及多个低价值低重量物品之和。所以应该使用动态规划

假设我们有 \(f(u, v)\) 。它表示拿前 \(u\) 件物品且剩余容量是 \(v\) 时能拿到的最大值,不难看出,\(f(N, M)\) 是这道题的答案。

那么我们就将求这道题的答案转化为了求 \(f(N, M)\)。我们首先需要把 \(f(u, v)\) 表示出来。

我们只关注于第 u 件物品,它有取与不取两种可能。若是取,则剩余 \(v-W_i\) 的空间,获得了 \(D_i\) 价值的物品。若是不取,则空间没有变化,得到的价值也没有变化。

不管取了还是不取,我们都对第u件物品做出了决策。我们需要在两个决策中选择一个最好的。所以我们需要知道两个决策的价值并比较。

对于取了的情况,我们得到的价值是剩余的空间取前 u-1 个物品的最大价值加上这件物品的价值。即 \(f(u-1, v-D_i)+W_i\) .

对于没有取的情况,我们得到的价值是目前的空间前 u-1 个物品的最大价值。即 \(f(u-1, v)\) .

最后,我们对两个决策之间选择一个最大值,就是 f(u, v) 的最优解

\[f(u,v) = \max\{f(u-1, v-D_i) + W_i, f(u-1, v)\} \]

这就是这道题的状态转移方程。

由于这道题是模板题,实际上所有的01背包方程都是基于这个方程推导出来的。

核心代码:

// C++
for(int i = 1; i <= N; i++) // 循环从1开始,可以避免处理越界行为,DP默认初始化0
{
    for(int j = 1; i <= M; j++) // 列举背包空间的情况
    {
        if (j < D[i]) // 在实际代码中,会有无法取的情况,这种情况就是不取
        {
            DP[i][j] = DP[i-1][j]; // 不取
        }
        else
        {
            DP[i][j] = std::max(DP[i-1][j-D[i]] + W[i], DP[i-1][j]); // 状态转移方程
        }
    }
}

优化:

仔细观察状态转移方程。发现对于 \(f(u,v)\) 有影响的只有 \(u-1\) ,所以我们就可以将状态转移方程压缩为一维,即一个参数。

假设有状态 \(f(v)\) 表示剩余空间为 \(v\) 时的最优解,写出状态转移方程:

\[f(v)=\max\{f(v), f(v-D_i)+W_i\} \]

优化后的核心代码:

for(int i = 1; i <= N; i++)
{
    for(int j = M; j >= W[i]; j--)
    {
        DP[j] = std::max(DP[j], DP[j - D[i]] + W[i]); // 状态转移方程
    }
}

一些练习题:

[P1049 NOIP2001 普及组] 装箱问题 - 洛谷

[P1048 NOIP2005 普及组] 采药 - 洛谷

2.1.2 完全背包

完全背包是背包问题的一种。它与01背包不同,01背包每类物品只能取一次,而完全背包每类物品能取无数次。

实际上,由于“背包容量”的限制,实际能取到的数量是有限的,这也是需要决策取舍的地方。

例题:

P1616 疯狂的采药 - 洛谷

题目大意:

在总共 \(t\) 的时间内,对 \(m\) 种草药中进行无数量限制的采摘。对于每类草药,有 \(A_i\) 和 \(B_i\) 分别表示草药的采集时间和价值。

解题思路:

这是一个完全背包问题。我们将决策从取与不取转换到取多少。我们用 \(k\) 来表示取 \(k\) 个草药。

有状态 \(f(u,v)\) 。表示在剩余背包空间为 \(v\) 时对前 \(u\) 种草药做决策后的最优解。不难发现,\(f(m, t)\) 是本题答案。

如果对第 \(u\) 种草药取了 \(k\) 个,那么获得的收益为:

\[f(u-1, v-k \times A_i)+k \times B_i \]

特别地,如果没有取,则 \(k=0\) ,所以 \(k\) 的取值范围是 \([0, +\infty)\) .

状态转移方程:

\[f(u, v)=\max_{k=0}^{\infty}\{f(u-1, v-k \times A_i)+k \times B_i\} \]

核心代码:

for(int i = 1; i <= m; i++)
{
    for(int j = 1; j <= t; j++)
    {
        for(int k = 0; k * A[i] <= j; k++) // 如果不能取就没有必要增加取的数量
        {
            DP[i][j] = std::max(DP[i][j], DP[i-1][j-k*A[i]]+k*B[i]); // 状态转移方程
        }
    }
}

这是一个 \(O(n^3)\) 的算法。是否能像01背包一样优化?

实际上,对于 \(f(u,v)\) 可以不枚举 \(k\) ,即:

\[f(u,v)=\max\{f(u-1, v), f(u-1, v-Ai) + Bi\} \]

对于核心代码,与01背包的枚举顺序是相反的:

for(int i = 1; i <= m; i++)
{
    for(int j = 0; j <= t - A[i]; j++) // 与01背包相反的枚举顺序
    {
        DP[j + A[i]] = std::max(DP[j] + B[i], DP[j + A[i]]); // 状态转移方程
    }
}

时间复杂度 \(O(n^2)\) .

2.2 区间DP

区间DP是对一个区间进行合并,求合并操作后的最优解。

区间DP最经典的例题就是石子合并。实际上这道题还可以进行四边形不等式优化,在这里不展开,参见后文DP优化。

例题:

P1880 [NOI1995] 石子合并 - 洛谷

题目大意:

在一个圆形操场的四周摆放 \(N\) 堆石子,现要将石子有次序地合并成一堆,规定每次只能选相邻的 2 堆合并成新的一堆,并将新的一堆的石子数,记为该次合并的得分。求得分的最大值和最小值。

样例输入:

4
4 5 9 4

样例输出:

43
54

解题思路:

这是一道区间DP模板题。非常经典。我们先不考虑环形的问题。假设他是直线的。

对于这道题,有状态转移方程 \(f(i,j)\) 表示在区间 \(i\) 到 \(j\) 之内合并后的最大值。显然,对于这道题的无环情况,\(f(0, N-1)\) 是答案。

仍然是表示状态转移方程。对于区间 \(i\) 到 \(j\) ,我们假设由一个下标 \(k\) 。它将区间 \(i\) 到 \(j\) 由 \(k\) 分隔开成两份。左右两边份分别合并成一堆,最后整体合并成一堆。那么求 \(k\) 的最优解,就是求 \(f(i, j)\) 的最优解。

我们使用枚举法:

\[f(i, j)=\max\{f(i, k) + f(k + 1, j) + cost\} \]

其中:\(k\) 从 \(i\) 到 \(j-1\) 进行枚举。cost是合并 \(i\) 到 \(j\) 后获得的收益。对于此题,\(cost=\sum_{n=i}^jA_n\) 。\(A_n\) 表示第 \(n\) 堆小石子的数量。

然后设 \(sum\) 为 \(A\) 的前缀和。状态转移方程转化为:

\[f(i, j)=\max\{f(i, k) + f(k + 1, j) + sum(j) - sum(i-1)\} \]

随后我们设 \(length=j-i-1\) 。那么枚举所有的 \(length\) 。随后枚举所有的 \(i\) ,求出 \(j\) 。列举所有的 \(k\) 进行状态转移。时间复杂度 \(O(n^3)\) 。

如果你看到这里想要试一试,不妨尝试这道简化版无环:

P1775 石子合并(弱化版) - 洛谷

如何处理环?

我们将原数组 \(A\) 扩展到原来的二倍。\(A_n = A_{2n}\) 。随后我们使用动态规划求解。最终的答案为:

\[answer=\max\{f(a + 1, a + n)\} (a\in[0, N-2]) \]

核心代码:

for(int l = 2; l <= N; l ++) // 枚举所有可能的长度
{
    for(int i = 1; i <= 2 * N + 1 - l; i++) // 枚举所有的i
    {
        int j = l + i - 1; // 通过长度与i计算j
        for(int k = i; k < j; k++) // 枚举所有可能的k
        {
            DP[i][j] = std::max(DP[i][j], DP[i][k] + DP[k + 1][j] + sum[j] - sum[i - 1]); // 状态转移方程
        }
    }
}

标签:01,max,理解,背包,物品,动态,规划,DP
From: https://www.cnblogs.com/liserver/p/18352522

相关文章

  • vue3中piniaPluginPersistedstate解决动态路由刷新空白问题
    总结:使用了回调函数来防止持久化数据前就渲染页面,导致刷新空白1.Store里的代码(**这里主要就是在addNewRoute写了回调callback**)addNewRoute(menuList,()=>{//重新渲染router.push({path:'/home/individual'})});import{defineStore}from"pinia";import......
  • Mybatis动态SQL
    1.动态SQL编写1.1if标签if标签是我们最常使用的。在查询、删除、更新的时候很可能会使用到。必须结合 test 属性联合使用a.在WHERE条件中使用if标签这是常见的一种现象,我们在进行按条件查询的时候,可能会有多种情况。在此SQL语句中,where1=1是多条件拼接时的......
  • Python网络爬虫抓取动态网页并将数据存入数据库MySQL
    简述以下的代码是使用python实现的网络爬虫,抓取动态网页http://hb.qq.com/baoliao/。此网页中的最新、精华下面的内容是由JavaScript动态生成的。审查网页元素与网页源码是不同。以上是网页源码以上是审查网页元素所以此处不能简单的使用正则表达式来获取内容。......
  • Flow -- 理解(面试官最爱问的7个Flow问题)
    1、问题: 请解释Flow是什么,与传统的RxJava相比有何优势?出发点:在回答这个问题时,应当强调对Flow的理解以及与RxJava的对比。涉及到Flow的背后原理、冷流、热流的概念,以及在响应式编程中的应用场景。参考简答:Flow是一种基于协程的响应式编程库,用于处理异步数据流。与RxJava相......
  • useImperativeHandle 是什么?你可以理解为 vue3 的 expose
    useImperativeHandle确实类似于Vue3的expose,两者都用于控制子组件向父组件暴露的接口。在React中,useImperativeHandle需要与forwardRef一起使用,原因如下:转发引用:forwardRef允许父组件将ref传递给子组件。没有forwardRef,父组件无法直接访问子组件的ref。......
  • 深入理解Python的模块和包
    目录模块简介创建和使用模块定义模块导入模块模块的搜索路径使用内置模块包简介创建和使用包定义包导入包相对导入和绝对导入模块和包的高级特性模块的重新加载模块的私有属性和函数包的动态导入实际项目中的应用项目结构设计模块化代码的好处总结模块简介在Pyth......
  • matlab求解线性规划问题
    在人们的生产实践中,经常会遇到如何利用现有资源来安排生产,以取得最大经济效益的问题。此类问题构成了运筹学的一个重要分支--数学规划,而线性规划(LinearProgramming,LP)则是数学规划的一个重要分支。本章会介绍线性规划模型与matlab求解目录一、线性规划的标准形二、linprog函......
  • 下载量10w+!大型语言模型:语言理解和生成
    近年来,人工智能在新语言能力方面取得了显著进展,深度学习技术的快速发展推动了语言AI系统在文本编写和理解方面的表现。免费获取:下载量10w+!大型语言模型:语言理解和生成......
  • 爆火下28万次!MIT最新-理解深度学习
        最近疯传的-理解深度学习-高达28万次,被认为可能。涵盖了深度学习从基础到高级各个方面的内容,包括基本概念、监督学习、强化学习、线性回归、神经网络、扩散模型等等。全面系统地机器学习的基础概念和深度学习的多种模型,还包括最新的Transformer和图神经网络。 免......
  • 【深入理解SpringCloud微服务】Ribbon源码解析
    【深入理解SpringCloud微服务】Ribbon源码解析Ribbon的原理RestTemplate中的拦截器链Ribbon的拦截器如何将拦截器放入到RestTemplate中Ribbon中的核心类LoadBalancerAutoConfigurationLoadBalancerInterceptorLoadBalancerClientILoadBalancerServerListIRuleIPingRibb......