动态规划(1)背包问题1——0/1背包,完全背包

本文介绍了动态规划中的基础问题——背包问题,包括0/1背包和完全背包。0/1背包特点是每种物品只能取一个或不取,通过二维数组或一维数组优化求解最大价值。完全背包则不限制每种物品的数量。文章提供了状态转移方程、代码实现及复杂度分析,帮助理解动态规划的核心思想。
摘要由CSDN通过智能技术生成

综述

背包问题是动态规划中较为基础的一种问题,也是初学者学习动态规划时第一会学到的问题

背包问题只在有限的容量体积或称重下,放入背包有体积,重量,价值的物体,求在不超过背包限制下的价值最大。

根据不同的题目要求,可以把背包问题分为五大类——0/1背包,完全背包,多重背包,分组背包以及综合前四种的混合背包;其难度也是逐层递增

术语声明(后文符号所表示的含义)

物品的价值——v[  ]

物品的重量/体积——w[  ]

——第i件物品;

——总重不超过j;

f[ i ][ j ]——总重不超过j的前i件物品价值最大值     (二维数组)

f[ i ]——前i件物品价值最大   (一维数组优化)

0/1背包

0/1背包特点即是所有物品只能去一个或者是不取,即是取(1)/不取(0)

二维数组求解

假设第i阶段处理第i种物品,前i-1种物品已处理完毕(不影响第i次操作),只需要考虑第i-1次向第i次转移 

状态表示  f[ i ][ j ]表示将前i件物品放入容量为j中获得的最大价值

第i中物品处理方式只有两种,即放或不放

①不放:放入背包的价值不增加,问题转化为求前i-1个物品放入容量为j的背包中的最大价值             f[ i ][ j ]=f[ i-1 ][ j ]

:问题转化为求前i-1种物品放入容量为j-w[ i ]的背包中的最大价值 

   f[ i ][ j ]=f[ i-1 ][ j-w[ i ] ]

    j-w[ i ]的意义——先把第i件物品的重量排开,则前i-1件物品的重量+第i件物品的重量<背包容量j,否则将不符合这一步数组下标的意义

不知道读者有没有发现,上述讨论的情况仅是在背包容量的空间能装下第i件物品时才成立

(即j>=w[ i ])那如果已经无法装下第i件物品,则问题转化为求前i-1个物品放入容量为j的背包中的最大价值   即   f[ i ][ j ]=f[ i-1 ][ j ]

综上,将上述三种情况全部合起来看,就可以得出0/1背包用二维数组完成下的状态转移方程

① j<w(i)      f[ i ][ j ]=f[ i-1 ][ j ]      。

② j>=w(i)     f[ i ][ j ]=max(f[ i-1 ][ j ],f[ i-1 ][ j-w[ i ] )

代码实现

void dp01()//0/1背包
{
	for(int i=1;i<=n;i++)
		for(int j=1;j<=W;j++)
		{
			if(j<w[i])
			     //若第i种物品的重量加入后大于背包容量,则不放入
			f[i][j]=f[i-1][j];
			else if(j>=w[i])
			{
				f[i][j]=max(f[i-1][j],f[i-1][j-w[i]]+v[i]);
			} 
		}
	cout<<"背包最大价值为 :"<<f[n][W];
	            //全部n种物品在背包容量为W时的价值最大值(即答案) 
} 

复杂度分析

本算法使用两层循环,时间复杂度为O(nW)。使用的是二维数组,空间复杂度为O(nW)。

一维数组优化

仔细思考就可以发现,每一次的f[ i ][ j ]只与f[ i-1 ][ j ]或者f[ i-1 ][ j-w[ i ] ]的值有关,即二维数组的上一排有关。且纵坐标(第二个括号内的数)都小于求的纵坐标(j>j-1  , j>j-w[ i ])所以可以用一维数组每次只保存上一排的数据(若不需要求最优解具体方案),对算法进行优化

根据二维数组的方式,归纳总结后就可以较为简单的得到一维数组解法的状态转移方程

f[ j ]=max(f[ j ],f[ j-w[ i ] ]+v[ i ])

void onezeropack()//0/1背包一维数组优化 
{
	for(int i=1;i<=n;i++)
		for(int j=W;j>=w[i];j--)//倒推 
			f[j]=max(f[j],f[j-w[i]]+v[i]);
}

以一道例题具体解释

问题描述

许多年前,在泰迪的家乡,有一个人被称为“骨头收藏家”。这个男人喜欢收集各种各样的骨头,比如狗狗,牛,还有他去了坟墓......
骨头收藏家有一个大容量的V袋,沿着他的收集之旅有很多骨头,显然,不同的骨骼具有不同的值和不同的体积,现在根据他的行程给出每个骨骼的值,你能计算出骨骼采集器可以得到的总值的最大值吗?

输入

第一行包含整数T。
接下来是T个案例,每个案例有三行,第一行包含两个整数N,V,(N <= 1000,V <= 1000)表示骨骼的数量和他的包的体积。第二行包含表示每个骨骼值的N个整数。第三行包含表示每个骨骼体积的N个整数。

输出

每行一个整数表示总值的最大值

样本输入

1
5 10
1 2 3 4 5
5 4 3 2 1

样本输出

14

                                                                               来源——HDU2602 骨头收藏家

原题网址     Problem - 2602

不过多解释二维数组的解法,直接上代码

#include<bits/stdc++.h>
using namespace std;
int f[1001][1001],t,v[1001],w[1001],n,W;
void dp01();
int main()
{
	cin>>t;
	while(t--)
	{
		memset(f,0,sizeof(f));
		memset(v,0,sizeof(v));
		memset(w,0,sizeof(w));//注意多组数据要在每次都初始化
		cin>>n>>W;
		for(int i=1;i<=n;i++) cin>>v[i];
		for(int i=1;i<=n;i++) cin>>w[i];
		dp01();
	}
}
void dp01()//0/1背包
{
	for(int i=1;i<=n;i++)
		for(int j=0;j<=W;j++)//本题的坑——骨头体积可能为零! 
		{
			if(j<w[i])
			     //若第i种物品的重量加入后大于背包容量,则不放入
			f[i][j]=f[i-1][j];
			else if(j>=w[i])
			{
				f[i][j]=max(f[i-1][j],f[i-1][j-w[i]]+v[i]);
			} 
		}
	cout<<f[n][W];
	            //全部n种物品在背包容量为W时的价值最大值(即答案) 
} 

下面主要是通过这个题详细介绍一下如何用一维数组优化算法求解

样例输入数据整理

物品 1 2 3 4 5
价值(v) 1 2 3 4 5
重量(w) 5 4 3 2 1

先对样例进行模拟

①当i=1  j∈[ 5,10 ]          w[ i ]=w[ 1 ]=5          v[ i ]=v[ 1 ]=1

j F[ j ]  
0 0 \
1 0 \
2 0 \
3 0
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值