【动态规划(三)】经典背包问题

1.1介绍

背包问题属于比较经典的动态规划问题,此处只记录了比较基础的三类,供初学者参考练习。较为复杂的背包问题可以阅读dd大牛的《背包九讲》,讲解的比较详细,当然也有一定的难度,也不需要一开始就要求自己全部精通,学习应量力而行,循序渐进。

1.2 01背包问题

1.2.1问题及其解析

问题:一个背包总容量为V,现在有N个物品,第i个 物品体积为weight[i],价值为value[i],现在往背包里面装东西,怎么装能使背包的内物品价值最大?
(1) 为何称为01背包问题?所谓01其实就是对于某个物品选还是不选,所以下次再听见“To be or not to be”就应该想到,这不是哲学问题而是个01问题(这大概属于Coder的特殊脑回路)。
(2) 假设A是N个物品之一,那么怎么确定选不选A呢?
(3) 首先,A的体积要小于V;
(4) 其次,加入后A的整体价值要大于没加入时的整体价值;
(5) 运用动态规划的思想,V0=1。。。。。。V-1,最后V0=V时,是基于某一个V0对应的体积和整体价值。
(6) 某一个V0是哪一个V0?V0=V-Weight[i]。

(7) 也就得到该方程:Dp[i][j]=Max(Dp[i-1][j],Dp[i-1][j-Weight[i]]+Vlaue[i]);

1.2.2源码

此处使用定值!

#include<iostream>
using namespace std;
#define V 10
#define N 5
#define max(a,b) (a)>(b)?(a):(b)
double Dp[N+1][V+1];
int main(){
	int Weight[N+1]={0,2,3,1,2,5}; //第一个不用
	int Value[N+1]={0,2,2,4,5,1};
	int i,j;
	
	for(i=1;i<=N;i++){
		for(j=1;j<=V;j++){
			if(Weight[i]<=j){
				Dp[i][j]=max(Dp[i-1][j],Value[i]+Dp[i-1][j-Weight[i]]);
			}
			else
				Dp[i][j]=Dp[i-1][j];
		}
	}
	cout <<Dp[N][V];
	return 0;
}

注释:一种优化的解法

(1)      这里简化成了一维数组Dp。

(2)      求Dp[i]时只需知道上一个(Dp[i-1])是什么状态。

(3)      最直观的方法是设断点去看Dp数组的值,其实5次大循环,对应上述二维数组是Dp[i]=Dp[i][],对照着小面给出的结果就很明了了。

(4)      为什么内循环是倒叙?因为需要确保Dp[i][j]的值来源于Dp[i-1][j-Weight[i]]。不会更改到小于Weight[i]的值。

#include<iostream>
using namespace std;
#define V 10
#define N 5
#define max(a,b) (a)>(b)?(a):(b)
double Dp[V+1]//全局变量,自动初始化为0 
int main(){
	int Weight[N+1]={0,2,3,1,2,5}; //第一个不用
	int Value[N+1]={0,2,2,4,5,1};
	int i,j;
	  for (int i=1; i<=N; i++)  {
        for (int j=V; j>=1; j--)  
        {  
            if (Weight[i]<=j)  
            {  
                Dp[j]=max(Dp [j],Dp[j-Weight[i]]+Value[i]);  
            }             
        }  
	  } 
    cout<< Dp [V]<<endl;//输出最优解 
	return 0;
}

1.2.3 结果

行开头为:行号(重量,价值),列为1到V的变化


1.3完全背包问题

1.3.1解析

问题:有N种物品和一个容量为V的背包,每种物品都有无限件可用。第i种物品的体积为weight[i],价值为value[i]。求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大。

(1)      与01背包问题不同,这次某个物品不只一个,而是有无限个。

(2)      之前是Dp[i][j]=Max(Dp[i-1][j],Dp[i-1][j-Weight[i]]+Vlaue[i]);而现在应该是Dp[i][j]=Max(Dp[i-1][j],Dp[i][j-Weight[i]]+Vlaue[i]);

(3)      为什么?因为有无限个,不需要再去讨论它之前有没有放入过了,而01的想法是这次放入那么意味着上次没有这个,所以 Dp[i-1]。

1.3.2源码
#include<iostream>
using namespace std;
#define V 10
#define N 5
#define max(a,b) (a)>(b)?(a):(b)
double Dp[N+1][V+1];  
int main(){
	int Weight[N+1]={0,2,3,1,2,5}; //第一个不用
	int Value[N+1]={0,2,2,4,5,1};
	int i,j;
	for(i=1;i<=N;i++){
		for(j=1;j<=V;j++){
			if(Weight[i]<=j){
						Dp[i][j]=max(Dp[i][j],Value[i]+Dp[i][j-Weight[i]]);
			}
		}
	}
	cout <<Dp[N][V]; 
	return 0;
}

注释:一种优化的解法

(1)      内循环在01背包问题优化的情况下改成顺序循环即可

(2)      为什么?不需要确保Dp[i][j]的值来基于Dp[i-1][j-Weight[i]]

#include<iostream>
using namespace std;
#define V 10
#define N 5
#define max(a,b) (a)>(b)?(a):(b)
double Dp[V+1] 
//全局变量,自动初始化为0  
int main(){
	int Weight[N+1]={0,2,3,1,2,5}; //第一个不用
	int Value[N+1]={0,2,2,4,5,1};
	int i,j;
	 for (int i=1; i<=N; i++)  
        for (int j=1; j<=V; j++)  
        {  
            if (Weight[i]<=j)  
            {  
                f[j]=max(f[j],f[j-Weight[i]]+Value[i]);  
            }             
        }  
      
    cout<<f[V]<<endl;//输出最优解  
	return 0;
}
1.3.4 结果


1.4总结

如果不明白的可以多跑跑断点看看,自己多设置几种情况,看看结果有什么区别,或者对照着结果表多推到一下,然后再去想代码的事情。这里是一些浅薄的想法,讲解不清或是谬误,还请指教和包容。





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值