动态规划(介绍闫氏dp分析法及相关例题分析)

本文介绍了动态规划的基本思路,特别是闫氏dp分析法,通过01背包问题、完全背包问题和区间dp问题为例进行深入分析。详细阐述了如何运用状态表示和状态计算来解决这些问题,并提供了实例代码,帮助读者理解和掌握动态规划的解决技巧。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

基本思路介绍:

 首先是在看问题时,知道这道题要用dp去做,但是怎么去实现呢?空想是很难去构造式子的,闫氏dp方法的核心就是用集合的方法去分析问题,从集合的角度入手去解决问题。
 新手入门一般就是求有限集合中的最值,比如大家常见的求一堆符合要求的方案中的最优值,这里就要分为两个阶段:化零为整,化整为零。第一是找出符合要求的方案,二是找出这些方案中的最优值(但有些题目也可能要让你找最优解、第k解,不定)。
    闫氏dp分析法流程图:
在这里插入图片描述

1)状态表示 (对应化零为整的过程):我们每次去处理问题的时候,不是一个元素一个元素去枚举,而是每次枚举一类东西,比如去枚举一个子集,就是将一些有相似点的元素化成一个子集,然后用某一个状态来表示。
状态表示分为集合和属性:
 集合:所有满足xx条件的集合,这就是 dp 数组所表示的集合。
 属性(一般是最大值、最小值、个数、存在性)。

2)状态计算(对应化整为零的过程):我们要了解每个状态如何去算出来。这些子集一般要满足两个划分原则:不重复、不遗漏。先搞清楚 dp [ i ] 所表示的状态是什么,然后将其划分为几个部分,然后每个部分分别去求。相当于将 dp [ i ] 划分为若干个子集。比方说我们dp [ i ] 求最大值,那么我们先去求一下每一个子集的最大值,再对它们取一个max 就行了,这就相当于把一个整体问题转化成某些子集的问题去处理。

ps:划分原则也不是绝对的,比如“不重复”,在一些情况下是允许重复的,前提是不影响结果,比如求最大值。
集合划分的依据(常用套路):寻找最后一个不同点。
 dp问题有很多种不同的模型,我们应先把各种dp模型整理出来,比如选择问题(背包模型),序列问题(最长上升子序列),状态压缩,状态积,区间dp,树形dp,单调积的优化,斜率优化,每个问题都要去整理一遍,遇到题目才会有经验去做,这是dp与其他章节不一样的地方。

用闫氏dp法进行例题分析:

01背包问题

题目大意:

有 N 件物品和一个容量是 V 的背包。每件物品只能使用一次。
第 i 件物品的体积是 v [ i ] ,价值是 w [ i ]。
求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。
输出最大价值。

输入格式:

第一行两个整数,N,V,用空格隔开,分别表示物品数量和背包容积。
接下来有 N 行,每行两个整数 vi,wi,用空格隔开,分别表示第 i 件物品的体积和价值。

输出格式

输出一个整数,表示能取到的最大价值。

数据范围:

0< N,V ≤1000
0< vi , wi ≤1000

输入样例
4 5
1 2
2 4
3 4
4 5
输出样例
8

用闫氏分析法
分析01背包问题:
1、分析:
有限集合求最值,物品数量是有限的,是 n 个,总共的容量也是有限的,最大容量是 v 。我们要选一些物品装进背包,保证总体积不超过 v ,每个物品要么选要么不选两种情况,所以总共选法的数量最多就是2 ^ n 。我们从 2 ^ n 种情况里面找到能使总价值最大的方案。

状态表示:用 dp [ i ] [ j ] 表示从前 i 个物体中选取且总体积不超过 j 的选法。
状态计算:状态划分为以下两种情况:
①不选第 i 个物品,此时 dp [ i ] [ j ] = dp [ i - 1 ] [ j ]
②选择第 i 个物品,那么就要选取 dp [ i - 1 ] [ j - v [ i ] ] 的情况再加上 w [ i ] ,此时 dp [ i ] [ j ] = dp [ i - 1 ] [ j - v [ [ i ] ] + w[ i ]
对这两种可能取一个最大值,总状态转移方程为:
dp [ i ] [ j ] = max ( dp [ i - 1 ] [ j ] , dp [ i - 1 ] [ j - v [ i ] ] + w [ i ] )
③注意:只有 j >= w [ i ] 时才有第二种情况,需加上判断。

2、用闫氏分析法做出流程图:
在这里插入图片描述
 如图做一个 dp [ i , j ] 集合,假设是一个椭圆,dp [ i , j ] 子集划分:通过找最后一个不同点,即选最后一个物品的状态不同,选第 i 个物品和不选第 i 个物品是不一样的。以这一点来划分整个集合。相当于把 dp [ i , j ] 划分为两个子集,左边为所有不选择第 i 个物品的方案 : dp [ i , j ] = dp [ i - 1 , j ] 。右边是所有选择第 i 个物品的方案 :dp [ i - 1 , j - v[ i ] ] + w [ i ] ]两边取 max 则是 dp [ i , j ]的最大值 :
dp [ i , j ] = max( dp [ i - 1 ] [ j ] , dp [ i - 1 ] [ j - v [ i ] ] + w[ i ] )。


朴素代码如下(后面有优化代码):

#include <stdio.h>
#include <iostream>
#include <string.h>
#define max(a, b) (a) > (b) ? (a) : (b);
const int maxn = 1010;
using namespace std;
int dp[maxn][maxn];
int w[maxn], v[maxn];//存物品的价值、体积

int main()
{
   
	memset(dp, 0 ,sizeof(dp));
	int n, m;//n为物品数量的,m为背包质量
	scanf("%d %d",&n, &m);
	for(int i = 1; i <= n; i++)
		scanf("%d %d",&v[i], &w[i]);
	for(int i = 1; i <= n; i++)
	{
   
		for(int j = 0; j <= m; j++)
		{
   
			dp[i][j] = dp[i-1][j];//左半边子集
			if(j >= v[i])//右半边子集不一定存在,需要进行判断
				dp[i][j] = max(dp[i][j], dp[i-1][j-v[i]] + w[i]);
		}
	}
	printf("%d\n",dp[n][m]);

	return 0;
}

完全背包

 由经验可得,一般第一维是考虑前i个物品,后几维则是各种限制条件。

 完全背包与01背包的区别在于每种物品都有无限件,而01背包问题每个物品最多只能用一次。
例题:

题目大意:
有 N 种物品和一个容量是 V 的背包,每种物品都有无限件可用。第 i 种物品的体积是 vi,价值是 wi。
求解哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。
输出最大价值。

输入格式:

第一行有两个整数,N,V用空格隔开,分别表示物品总数和背包容积。
接下来有N行,每行两个整数vi,wi,用空格隔开,分别表示第 i 种物品的体积和价值。

输出格式:

输出一个整数,表示最大值。

数据范围:

0 <= N, V <= 1000
0 <= vi, wi <=1000

输入样例:

4 5
1 2
2 4
3 4
4 5

输出样例:

10

再用闫氏dp分析法分析一波:
在这里插入图片描述

 状态计算:因为每种物品都有无限多个,所以不能像01背包那样将集合单纯分为两个子集(选或不选)。应将集合划分成若干个子集

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值