首页 > 其他分享 >动态规划之——背包DP(入门篇)

动态规划之——背包DP(入门篇)

时间:2024-08-05 13:53:04浏览次数:14  
标签:状态 背包 int max 入门篇 01 DP dp

文章目录

概要说明

本文只讲了01背包和完全背包,至于其他背包问题后续补充

01背包

模板例题

点击这里

题意概要

在这里插入图片描述

思路

01背包的模板题

首先对于背包问题,我们只有两种选择: 选或者不选 选或者不选 选或者不选
我们先设DP状态为 f ( n , W ) , n 代表选取物品的数量, W 代表当前背包可容纳的重量 f(n,W),n代表选取物品的数量,W代表当前背包可容纳的重量 f(n,W),n代表选取物品的数量,W代表当前背包可容纳的重量
我们可以先从后往前枚举,在当前 f ( n , W ) f(n,W) f(n,W)状态下,不选物品,那么 f ( n , W ) = f ( n − 1 , W ) f(n,W)=f(n-1,W) f(n,W)=f(n−1,W)
(解释:不选当前的物品,那么物品的总数返回前面一个状态,当前可容纳的重量不变)
选当前物品,那么 f ( n , W ) = f ( n − 1 , W − w [ i ] ) + v [ i ] f(n,W)=f(n-1,W-w[i])+v[i] f(n,W)=f(n−1,W−w[i])+v[i]
(解释:选当前物品,那么物品的总数返回前面一个状态,当前可容纳的重量减去物品重量,并且加上物品的价值)
剩下依次往前枚举,直到出现 f ( 0 , W ) = 0 或者 f ( n , 0 ) = 0 结束,即没有物品可以选和可容纳重量为 0 f(0,W)=0或者f(n,0)=0结束,即没有物品可以选和可容纳重量为0 f(0,W)=0或者f(n,0)=0结束,即没有物品可以选和可容纳重量为0

以上是记忆化搜索的步骤,我们可以将上述步骤改为递归
同样分为两种状态:选或者不选

  • 不选: d p [ i ] [ j ] = d p [ i − 1 ] [ j ] dp[i][j]=dp[i-1][j] dp[i][j]=dp[i−1][j]
  • 选: d p [ i − 1 ] [ j − v [ i ] ] + w [ i ] dp[i-1][j-v[i]]+w[i] dp[i−1][j−v[i]]+w[i]

那么它的状态转移方程就为: d p [ i ] [ j ] = m a x ( d p [ i − 1 ] [ j ] , d p [ i − 1 ] [ j − v [ i ] ] + w [ i ] ) dp[i][j]=max(dp[i-1][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])

接下来看代码~~

code1

void solve(){
	int n,m;
	cin >> n >> m;
	for(int i=1;i<=n;++i){
		cin >> v[i] >> w[i];
	}
	for(int i=1;i<=n;++i)
	   for(int j=1;j<=m;++j){
	   	if(j<v[i]) dp[i][j]=dp[i-1][j];//当前重量比v[i]小,那么只能不选
	   	else dp[i][j]=max(dp[i-1][j],dp[i-1][j-v[i]]+w[i]);
	   }
	cout << dp[n][m] << endl;
	return ;
}

对于上述例题来说,这个代码是过不去的
why?这个代码会超出内存限制
在这里插入图片描述

那么我们就得优化代码
观察式子,我们发现当前状态 d p [ i ] [ j ] 只跟 d p [ i − 1 ] [ j ] 有关 dp[i][j]只跟dp[i-1][j]有关 dp[i][j]只跟dp[i−1][j]有关,因此我们只需要保留一维,将新的状态覆盖在原状态即可
那么新的状态转移方程为: f [ l ] = m a x ( f [ l ] , f [ l − w [ i ] ] + v [ i ] ) , l 相当于上述的 j f[l]=max(f[l],f[l-w[i]]+v[i]),l相当于上述的j f[l]=max(f[l],f[l−w[i]]+v[i]),l相当于上述的j
(解释:新的状态每次都会覆盖原状态,因此每次都跟自己比较大小即可)

AC代码

code2

void solve(){
	int n,W;
	cin >> n >> W;
	for(int i=1;i<=n;++i){
		cin >> w[i] >> v[i];
	}
	for(int i=1;i<=n;++i)
	   for(int l=W;l>=w[i];--l){
	   	f[l]=max(f[l],f[l-w[i]]+v[i]);
	   }
	cout << f[W] << endl;
	return ;
}

注意: l 必须从后往前遍历 l必须从后往前遍历 l必须从后往前遍历
如果我们从前往后遍历,那么每次新状态覆盖原状态时,新状态又会覆盖新状态
什么意思呢?
其实我们每次覆盖时,当前那一层是不能覆盖当前那一层的状态的,只能后面一层覆盖前面一层的状态
前往后遍历,会导致当前层覆盖当前层的状态,这其实就是完全背包(后面来讲)
而我们从后往前遍历,后面的容量不可能比原来的容量大,因此也就不会出现上面这种情况

到此,模板题讲完了,接下来我们来看一道01应用题

01背包的应用题

题目来源

Q我~~~~

思路

01背包的变形题
分别对4个科目进行DP,首先我们很容易想到:

  • 左脑和右脑所花的时间尽可能相同
  • 单纯考虑左脑所花的时间,那么就是面临两种选择:做这题 or 不做这题
  • 所花的时间为总时间减去左脑所用的时间

首先定义总重W,W的值为单科所花的总时间的一半(尽可能让左右脑所花时间相同)
写出状态状态转移方程: f [ k ] = m a x ( f [ k ] , f [ k − b [ j ] ] + b [ j ] ) ; f[k]=max(f[k],f[k-b[j]]+b[j]); f[k]=max(f[k],f[k−b[j]]+b[j]);,与01背包不同的是,这题的重量和价值是相同的
算出左脑最多花的时间,ans加上总时间减去 f [ s u m / 2 ] f[sum/2] f[sum/2]即单科所花的时间(sum为总时间)

code

void solve(){
	for(int i=1;i<=4;++i) cin >> a[i];
	for(int i=1;i<=4;++i){
		int sum=0;
		for(int j=1;j<=a[i];++j){
			cin >> b[j];
			sum+=b[j];
		}
		for(int j=1;j<=a[i];++j)
		   for(int k=sum/2;k>=b[j];--k){
		   	f[k]=max(f[k],f[k-b[j]]+b[j]);
		   }
		ans+=sum-f[sum/2];
		for(int j=1;j<=sum/2;++j) f[j]=0;
	}
	cout << ans << endl;
	return ;
}

完全背包

模板例题

点这里~~

题意概要

在这里插入图片描述

思路

完全背包的状态转移方程和01背包是一模一样的,都为: f [ l ] = m a x ( f [ l ] , f [ l − w [ i ] ] + v [ i ] ) ; f[l]=max(f[l],f[l-w[i]]+v[i]); f[l]=max(f[l],f[l−w[i]]+v[i]);
区别就在于 l l l是从前往后遍历
例如:背包容量为10 有一个物品重量为1 价值为1
从前往后遍历, f [ 1 ] = m a x ( f [ 1 ] , f [ 1 − w [ 1 ] ] + v [ 1 ] ) = 1 f [ 2 ] = m a x ( f [ 2 ] , f [ 2 − w [ 1 ] ] + v [ 1 ] ) = 2 ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ f[1]=max(f[1],f[1-w[1]]+v[1])=1 f[2]=max(f[2],f[2-w[1]]+v[1])=2······· f[1]=max(f[1],f[1−w[1]]+v[1])=1f[2]=max(f[2],f[2−w[1]]+v[1])=2⋅⋅⋅⋅⋅⋅⋅
最终 f [ 10 ] = 10 f[10]=10 f[10]=10
相当于每次遍历都是在原来覆盖过的状态进行下一轮的覆盖

接下来看代码

code

void solve(){
	int W,n;
	cin >> W >> n;
	for(int i=1;i<=n;++i){
		cin >> w[i] >> v[i];
	}
	for(int i=1;i<=n;++i)
	   for(int l=w[i];l<=W;++l){
	   	f[l]=max(f[l],f[l-w[i]]+v[i]);
	   }
	cout << f[W];
	return ;
}

标签:状态,背包,int,max,入门篇,01,DP,dp
From: https://blog.csdn.net/wh233z/article/details/140919673

相关文章

  • 换根 dp
    板子P2986[USACO10MAR]GreatCowGatheringGP3478[POI2008]STA-StationP3047[USACO12FEB]NearbyCowsG很好的一题f[i][j]表示与点i(向下(点i儿子1中的点))距离为j的点的权值和g[i][j]表示与点i(所有)距离为j的点的权值和需要特别注意的是刚开始时g[i][j]=......
  • 状态压缩DP
    状态压缩DP定义:‌状态压缩是一种使用二进制数来表示状态的方法,通常用于表示只有两种状态(0和1)的对象。Acwing,291蒙特里安的梦想291.蒙德里安的梦想-AcWing题库题目概览求把N×......
  • 常见CMS漏洞(WordPress、DeDeCMS、ASPCMS、PHPMyadmin、Pageadmin)
    目录一:WordPress步骤一:进入Vulhub靶场并执行以下命令开启靶场;在浏览器中访问并安装好子...步骤二:思路是修改其WP的模板写入一句话木马后门并访问其文件即可GetShel;登陆WP后点击【外观】--》【编辑】--》404.php步骤三:访问以下连接即可获取WebShel...姿势二:上传主......
  • Luogu P1777 帮助 题解 [ 紫 ] [ 线性 dp ] [ 状压 dp ]
    帮助:大毒瘤!!!调了我2h,拍了我2h,最后没调出来,重写才AC。wdnmd。思路这题主要是线性dp,而状压dp只是最后在统计答案时的一个辅助。首先定义\(dp[i][j][k]\)为考虑前\(i\)本书,已经抽出来了\(j\)本,最后没被抽出来的一本书是高度\(k\)的最小混乱度。每次放入的书与最后一位......
  • 背包计数问题的多项式优化
    此优化针对以下计数问题:n件物品,背包容量为m,第i件物品体积为\(a_i\),求装满的方案数。(01背包)n种物品,背包容量为m,第i件物品体积为\(a_i\),数量无限,求装满的方案数。(完全背包)n种物品,背包容量为m,第i件物品体积为\(a_i\),数量为\(b_i\),求装满的方案数。(多重背包)\((1\l......
  • 简析传输层协议——TCP、UDP协议
    TCP/IP协议族的传输层协议TCP(TransmissionControlProtocol)传输控制协议UDP(UserDatagramProtocol)用户数据报协议TCP协议介绍:TCP是面向连接的、可靠的进程到进程通信的协议TCP提供全双工服务,即数据可在同一时间双向传输TCP报文段:TCP将若干个字节构成一个分......
  • DP
    1)数位DP数位DP:用来解决一类特定问题,这种问题比较好辨认,一般具有这几个特征:要求统计满足一定条件的数的数量(即,最终目的为计数);这些条件经过转化后可以使用「数位」的思想去理解和判断;输入会提供一个数字区间(有时也只提供上界)来作为统计的限制;上界很大(比如10^{18}),暴力枚举......
  • C++动态规划(01背包)
    例题1:有 N 个物品,从 1 到 N 编号。对于每个 i(1≤i≤N),物品 i 的重量是 wi​ 价值是 vi​。太郎决定从 N 个物品里选一些放进背包带回家。太郎的背包的容量是 W,因此放进背包的物品的总重量不能超过 W。求太郎带回家的物品的总价值可能达到的最大值。1.贪......
  • centos7上dpdk绑定vfio-pci失败
    记一次使用dpdk中的报错:运行dpdk/usertools/dpdk-devbind.py-bvfio-pci02:05.0来绑定设备到vfio-pci时,报出了如下错误:Error:bindfailedfor0000:02:05.0-Cannotbindtodrivervfio-pci:[Errno19]NosuchdeviceError:unbindfailedfor0000:02:05.0-Cannot......
  • Luogu P5005 中国象棋 - 摆上马 / Luogu P8756 国际象棋 题解 [ 蓝 ] [ 状压 dp ] [
    国际象棋:模板棋盘状压。摆上马:需要点思维的棋盘状压,相比上一道题加了“蹩马脚”的设定。Easy_version:国际象棋概述一下此类棋盘问题的思路:用二进制数表示出棋盘上某一行的状态。用位运算预处理出合法的单行状态,以及需要用到的一些东西。用位运算判断前一行或者前几行能否......