动态规划入门(一):简单01背包

思路来源:https://blog.csdn.net/yoer77/article/details/70943462

动态规划:

动态规划算法是一种根据先决量来推导后量的一种算法,它的核心思想是状态转移:每一个新的状态量都可以通过已知的一个旧的状态量来推得,例如,当我们想要知道选取三件物品的最优解时,可以从选取两件物品的最优解中推导过来,以此类推。

 

(一)01背包基础知识:

在介绍01背包前,先介绍一下背包问题,什么是背包问题呢?它又和动态规划有什么关系呢?我们来举个最经典的例子:

 

假如现在有四件物品,重量为1,2,3,4 价值为3,2,4,5 ,而现在我们有一个承重为6的背包,那我们该怎样选取物品才能保证我们得到的物品总价值最大呢?而动态规划算法就是为了解决类似的问题而诞生的。

 

算法分析:我们可以开辟一个二维数组dp[MAX_N][MAX_W]来帮助我们去得到这个问题的最终答案,

dp[i][j]表示的是在前i件物品,背包j大小时的最优解

例如:dp[2][3]记录的就是在只有前两件物品时,背包只有大小3时的最优解

具体代码:

void DP(int n,int m){
	for(int i=1;i<=n;i++){//i是物品数 
		for(int j=1;j<=m;j++){//j是物品承重 
			if(j<w[i])
				dp[i][j]=dp[i-1][j];
			else
				dp[i][j]=max(dp[i-1][j],dp[i-1][j-w[i]]+v[i]);
		}
	}
}

一层for:物品遍历:从第1件物品开始遍历到第n件物品

二层for:承重遍历:从承重1开始遍历到承重w ,将每种承重下的最优解记录到dp数组中

对于每种状态:

if 当前承重<当前物品重量(太沉了装不下):这种状态下这件物品是放不进背包的,那么直接让这一轮的dp[j]=上一轮的dp[j],就好,换言之,其实就是不把这件物品放进去

else(如果能装下):这时我们就要比较了,是上一轮的当前承重下的总价值最大,还是把当前这间物品塞进去以后更大,哪个大取哪个,上一轮的状态是dp[i-1][j],而将这件物品塞进去的状态是dp[];

最后当把所有的物品遍历完后,dp[n][1—w]这个数组里就是每一种承重下的最大value,遍历一遍则得到最大value;

case 1:(上面的例子)

row1:4件物品 总承重6

row2-rown+1:weight、value

else:dp数组[1-n][1-w]

 

case 2:

row1:5 件物品 总承重10

row2-rown+1:value、weight

else:dp数组[1-n][1-w]

下面讲一下空间复杂度优化后算法

根据大佬们的说法:第i行存的是前i种物品下j承重的最大value,那么如果我们求的是所有物品(也就是所谓的n件物品)的最大value的话,其实我们只需要用最后一行来存就可以了,但是问题在于我们在讨论第i件物品时,需要i-1时的状态来帮助我们往下推,那么怎么解决这个问题呢?其实很简单(简单个P),就是在每次讨论第i件物品的开始(第二个for循环开始),这时的数组里存的还是i-1时的状态,我们每次遍历时其实用到的都是[i-1][j]或者[i-1][j-w[i]],能够发现用的都是比此称重下小的状态

所以!我们只需要反着遍历就可以了,从承重w—1,这样就ok了,我们在用到之前状态时,还没覆盖;

具体代码如下:

for(int i=1;i<=N;i++)//i是物品数 
	{
		for(int j=W;j>=0;j--)//j是物品承重 
		{
			if(j<w[i])continue;
			else
				dp[j]=max(dp[j],dp[j-w[i]]+v[i]);
		}
	}

 

Case 3:

样例仍是Case 2的样例,但数组却只存储了最后一行,空间复杂度下降到O(n)

 

(二)例题与讲解:

1、原题链接:https://www.luogu.org/problemnew/show/P1060

思路分析:简单01背包模板题,背包大小就是n,物品的重量w就是价格,而价值v就是价格*重要度

AC代码:

#include<bits/stdc++.h>
#define MAX_N 30005
using namespace std;
int dp[MAX_N],w[30],v[30];
int main(){
	memset(dp,0,sizeof(dp));
	int m,n;//money number
	cin>>m>>n;
	for(int i=0;i<n;i++){
		int a,b;
		cin>>a>>b;
		w[i]=a;
		v[i]=a*b;
	}
	for(int i=0;i<n;i++){
		for(int j=m;j>w[i];j--){
			dp[j]=max(dp[j],dp[j-w[i]]+v[i]);
		}
	}
	cout<<dp[m]<<endl;
}

2、原题链接:https://www.luogu.org/problemnew/show/P1164

思路分析:这题问的是方案数,一般方案数的东西都是一个累加过程,当前状态数是上一种状态数的累加,这题根据题意可推出,在有3块钱买2块钱的东西时的方案数,就是你有1块钱的方案数;

状态转移方程为:dp[ j ]+=dp[ j-w[ i ] ]

AC代码:

#include<bits/stdc++.h>
#define MAX_N 10005
#define INF 0x3f3f3f3f
using namespace std;
int dp[MAX_N],w[105];
void init(){
	for(int i=0;i<MAX_N;i++)dp[i]=0;
}
int main(){
	init();
	dp[0]=1;
	int n,m;
	cin>>n>>m; 
	for(int i=0;i<n;i++){
		cin>>w[i];
	}
	for(int i=0;i<n;i++){
		for(int j=m;j>=w[i];j--){
			dp[j]+=dp[j-w[i]];
		}
	}
	cout<<dp[m]<<endl;
}

(未完待续···)

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值