第二节 背包

第二节 背包

学习背景

小Z从数字金字塔上下来后,来到了一座城堡,城堡里有各种各样的宝物,如果项带走某种宝物,搬运它需要一定的时间 w i w_i wi,当然宝物也会有自己的价值 v i v_i vi。可小Z赶时间,他在城堡里呆的时间有限,只有 t t t分钟,小Z应该搬走哪些宝物才能得到最多的价值呢?

小Z想,按性价比( v i / w i v_i/w_i vi/wi)对所有宝物排序,从高到低依次搬不就行了吗?但是小Z很快找到了一些反例,例如,小Z有10分钟,现有4件宝物,每件宝物的搬运时间分别为:4,3,4,7,每件宝物的价值分别是48,30,48,69。如果按性价比搬宝物,小Z会搬走第1件和第3件,得到的价值为96,但是小Z搬走第2件和第4件可以得到的价值为99。

突然,小Z想到可以运用本章第一节学习的DP来解决这个问题呀!你知道他是怎么解决的吗?

学习知识

背包问题是指这样一类问题:给定一些物品,每种物品都有自己的体积和价值,在限定的总体积(背包容量)内,选择哪些物品才能使物品的总价值最高。

(一)背包的基础类型

有三种基础的背包类型,分别是01背包、完全背包和多重背包,这三种类型的划分依据是每种物品的可选数量。对01背包而言,每种物品只有一件;对完全背包而言,每种物品有无限件;对多重背包而言,每种物品有有限件(可能为1,也可能为其他数字)。

1.01把背包

(1)01背包的模型

01背包的模型为:给定n种物品,其中第i种物品的体积为 w i w_i wi,价值为 v i v_i vi,每种物品只有一件。有一体积为m的背包,请选择一些物品放入背包,使得在物品总体积 不超过m的前提下,物品的价值总和最大。

  • 阶段划分。问题规模收n,m的限制,按什么划分阶段呢?试想,我们可以按物品顺序逐一考虑每件物品是否加入背包,因此,可按已处理物品的件数将选择过程划分为n个阶段,设阶段变量i代表前i件物品。

  • 设计状态。在考虑第i件物品放不放进背包时,还需要知道背包的容量情况,可用二位状态变量 ( i , j ) (i,j) (i,j)代表前 i i i件物品,背包体积为j,设最优值函数f代表背包物品的最大价值和。

    有两种状态定义方式:一是设 f [ i ] [ j ] f[i][j] f[i][j]代表前 i i i件物品选择若干件放入体积为 j j j的背包得到的最大价值和(体积 j j j可以不装满);二是设 f [ i ] [ j ] f[i][j] f[i][j]代表前 i i i件物品选择若干 件恰好放入体积为 j j j的背包得到的最大价值和(体积 j j j必须装满)。

    这两种状态定义在确定边界和目标解时会有区别,本问题会给出两种状态下的实现,但后续问题分析将采用第二种形式。

  • 确定对策。对于状态 f [ i ] [ j ] f[i][j] f[i][j],问题进入了第 i i i阶段,决策对象为第 i i i种物品,可以做出的决策有:选、不选。

  • 确定状态转移方程。
    f [ i ] [ j ] = m a x { f [ i − 1 ] [ j ] 不选第 i 种物品 f [ i − 1 ] [ j − w [ i ] ] + v [ i ] , j > = w [ i ] 选 1 件第 i 种物品 f[i][j]=max\begin{cases} f[i-1][j] & 不选第i种物品 \\ f[i-1][j-w[i]]+v[i],j>=w[i] & 选1件第i种物品 \end{cases} f[i][j]=max{f[i1][j]f[i1][jw[i]]+v[i],j>=w[i]不选第i种物品1件第i种物品

  • 确定边界。若状态 f [ i ] [ j ] f[i][j] f[i][j]的定义为第一种,边界 f [ i ] [ j ] = 0 f[i][j]=0 f[i][j]=0,即前i种物品选择若干件放入体积为j的背包(体积j可以不装满)得到的最大价值和为0,因为可以一件也不选。

    若状态 f [ i ] [ j ] f[i][j] f[i][j]的定义为第二种,边界为 f [ 0 ] [ 0 ] = 0 f[0][0]=0 f[0][0]=0,其余 f [ i ] [ j ] f[i][j] f[i][j]的值设为负无穷,表示前i种物品选择若干见恰好放入体积为j的背包(体积j必须刚好装满)不存在可行方案(无解),此时 f [ 0 ] [ 0 ] f[0][0] f[0][0]必须设为0,否则 f [ i ] [ j ] f[i][j] f[i][j]的值无法顺利更新。

  • 确定目标解。若状态 f [ i ] [ j ] f[i][j] f[i][j]的定义为第一种,目标解为 f [ n ] [ m ] f[n][m] f[n][m],即前n件物品选择若干件放入体积为m的背包(体积m可以不装满)得到的最大价值和。

    若状态 f [ i ] [ j ] f[i][j] f[i][j]定义为第二种,由于不确定最后放入的物品所占体积,因此须枚举j,在所有的 f [ n ] [ j ] f[n][j] f[n][j]中取最大值,因此目标解为 m a x ( f [ n ] [ j ] ) ( 0 < = j < = m ) max(f[n][j])(0<=j<=m) max(f[n][j])(0<=j<=m)

    【实战训练4-2-1】采药(题目来源:洛谷P1048,NOIP2005普及组)

    [NOIP2005 普及组] 采药

    题目描述

    辰辰是个天资聪颖的孩子,他的梦想是成为世界上最伟大的医师。为此,他想拜附近最有威望的医师为师。医师为了判断他的资质,给他出了一个难题。医师把他带到一个到处都是草药的山洞里对他说:“孩子,这个山洞里有一些不同的草药,采每一株都需要一些时间,每一株也有它自身的价值。我会给你一段时间,在这段时间里,你可以采到一些草药。如果你是一个聪明的孩子,你应该可以让采到的草药的总价值最大。”

    如果你是辰辰,你能完成这个任务吗?

    输入格式

    第一行有 2 2 2 个整数 T T T 1 ≤ T ≤ 1000 1 \le T \le 1000 1T1000)和 M M M 1 ≤ M ≤ 100 1 \le M \le 100 1M100),用一个空格隔开, T T T 代表总共能够用来采药的时间, M M M 代表山洞里的草药的数目。

    接下来的 M M M 行每行包括两个在 1 1 1 100 100 100 之间(包括 1 1 1 100 100 100)的整数,分别表示采摘某株草药的时间和这株草药的价值。

    输出格式

    输出在规定的时间内可以采到的草药的最大总价值。

    样例 #1

    样例输入 #1

    70 3
    71 100
    69 1
    1 2
    

    样例输出 #1

    3
    

    提示

    【数据范围】

    • 对于 30 % 30\% 30% 的数据, M ≤ 10 M \le 10 M10
    • 对于全部的数据, M ≤ 100 M \le 100 M100

    【题目来源】

    NOIP 2005 普及组第三题

【问题分析】本问题为01背包模板题,问题分析如上,下面给出两种状态定义下的代码实现。若状态 f [ i ] [ j ] f[i][j] f[i][j]代表前i种物品选择若干件放入体积为j的背包得到的最大价值和(体积j可以不装满),则有:

#include<bits/stdc++.h>
using namespace std;
int f[105][1005];
int w[105],v[105];
int main()
{
	int m,n;
	cin>>m>>n;
	for(int i=1;i<=n;i++){
		cin>>w[i]>>v[i];
	}
	for(int i=1;i<=n;i++){
		for(int j=0;j<=m;j++){
			f[i][j]=f[i-1][j];
			if(j>=w[i]){
				f[i][j]=max(f[i][j],f[i-1][j-w[i]]+v[i]);
			}
		}
	}
	cout<<f[n][m]<<endl;
	return 0;
}

若状态 f [ i ] [ j ] f[i][j] f[i][j]代表前i种物品选择若干件恰好放入体积为j的背包得到的最大价值和(体积 j必须装满),则有:

#include<bits/stdc++.h>
using namespace std;
int f[105][1005];
int w[105],v[105];
int main()
{
	int m,n;
	cin>>m>>n;
	for(int i=1;i<=n;i++){
		cin>>w[i]>>v[i];
	}
	memset(f,0xcf,sizeof(f));
	f[0][0]=0;
	for(int i=1;i<=n;i++){
		for(int j=0;j<=m;j++){
			f[i][j]=f[i-1][j];
			if(j>=w[i]){
				f[i][j]=max(f[i][j],f[i-1][j-w[i]]+v[i]);
			}
		}
	}
	int ans=-1e9;
	for(int i=0;i<=m;i++)ans=max(ans,f[n][i]);
	cout<<ans<<endl;
	return 0;
}

(2)01背包的优化

上述算法的时间复杂度、空间复杂度均为O(n*m),下面给出两种优化空间复杂度的方法。

  • 滚动数组优化

    观察状态转移方程 f [ i ] [ j ] = m a x ( f [ i − 1 ] [ j ] , f [ i − 1 ] [ j − w [ i ] ] + v [ i ] ) f[i][j]=max(f[i-1][j],f[i-1][j-w[i]]+v[i]) f[i][j]=max(f[i1][j],f[i1][jw[i]]+v[i]),每一阶段的状态的最优解只和上一阶段有关,而与更早之前的阶段无关。更形象地,以实战训练4-2-1的样例为例,DP过程中状态 f [ i ] [ j ] f[i][j] f[i][j]的值的变化情况如表4-2-1所示,表中箭头说明了第3阶段到第4阶段的状态转移情况,其中垂直箭头代表的决策是不选第4种物品,斜线箭头代表的决策是第4种物品。

在这里插入图片描述

因此,为了节省空间开销,我们只需要开辟数组 f [ 2 ] [ m ] f[2][m] f[2][m],让第 i i i阶段的状态存储在f[i&1][ j]中即可,当i为奇数时,i&1=1;当i为偶数时,i&1=0。状态f[i&1][ j]的值相当于在 f [ 0 ] [ j ] f[0][j] f[0][j] f [ 1 ] [ j ] f[1][j] f[1][j]中滚动存储,故名滚动数组优化,空间复杂度由O(n*m)降低到了O(m)。下面给出滚动数组优化参考代码:

#include<bits/stdc++.h>
using namespace std;
int f[2][1005];
int w[105],v[105];
int main()
{
	int m,n;
	cin>>m>>n;
	for(int i=1;i<=n;i++){
		cin>>w[i]>>v[i];
	}
	memset(f,0xcf,sizeof(f));
	f[0][0]=0;
	for(int i=1;i<=n;i++){
		for(int j=0;j<=m;j++){
			f[i&1][j]=f[(i-1)&1][j];
			if(j>=w[i]){
				f[i&1][j]=max(f[i&1][j],f[(i-1)&1][j-w[i]]+v[i]);
			}
		}
	}
	int ans=-1e9;
	for(int i=0;i<=m;i++)ans=max(ans,f[n&1][i]);
	cout<<ans<<endl;
	return 0;
}
  • 降维优化

    再次观察表4-2-1中第3阶段到第4阶段的状态转移情况,发现第4阶段的每一个状态首先都会由一个垂直箭头(不选第4种物品)转移,再看能否由倾斜箭头(选第4种物品)转移。

    更一般地,若不选第i种物品,第i阶段所有状态的值会与第i-1阶段所有状态的值一样,等价于将 f [ i − 1 ] [ j ] f[i-1][j] f[i1][j]复制了一份给 f [ i ] [ j ] f[i][j] f[i][j],因此可进一步地将滚动数组 f [ 2 ] [ m ] f[2][m] f[2][m]种的第一维也省略,令f[j]代表前i种物品选择若干件恰好装入体积为j的背包的最大价值和(体积为j必须装满)。

    降维代码如下:

    #include<bits/stdc++.h>
    using namespace std;
    int f[1005];
    int w[105],v[105];
    int main()
    {
    	int m,n;
    	cin>>m>>n;
    	for(int i=1;i<=n;i++){
    		cin>>w[i]>>v[i];
    	}
    	memset(f,0xcf,sizeof(f));
    	f[0]=0;
    	for(int i=1;i<=n;i++){
    		for(int j=m;j>=w[i];j--){
    			f[j]=max(f[j],f[j-w[i]]+v[i]);
    		}
    	}
    	int ans=-1e9;
    	for(int i=0;i<=m;i++)ans=max(ans,f[i]);
    	cout<<ans<<endl;
    	return 0;
    }
    
2.完全背包

(1)完全背包模型

完全背包的模型为:给定n种物品,其中第i个物品的体积为 w i w_i wi,价值为 v i v_i vi,每种物品有无数件。有一体积为m的背包,请选择一些物品放入背包,使得在物品总体积不超过m的前提下,物品的价值总和最大。

  • 阶段划分。与01背包一样,完全背包仍然按已处理物品种数划分阶段,设阶段变量i代表前i种物品。
  • 设计状态。设 f [ i ] [ j ] f[i][j] f[i][j]代表前i种物品选择若干件恰好放入体积为j的背包的最大价值和(体积j必须装满)。
  • 确定决策。对于状态 f [ i ] [ j ] f[i][j] f[i][j],问题进入第i阶段,决策对象为第i种物品,做出的决策即第i种物品选几件,可以为0件,1件,2件,……,j/w[i]件。设决策变量为k,那么决策变量k的范围为 0 < = k < = j / w [ i ] 0<=k<=j/w[i] 0<=k<=j/w[i]
  • 确定状态转移方程。 f [ i ] [ j ] = m a x ( f [ i ] [ j ] , f [ i − 1 ] [ j − k ∗ w [ i ] ] + k ∗ v [ i ] ) ( 0 < = k < = j / w [ i ] ) f[i][j]=max(f[i][j],f[i-1][j-k*w[i]]+k*v[i]) (0<=k<=j/w[i]) f[i][j]=max(f[i][j],f[i1][jkw[i]]+kv[i])(0<=k<=j/w[i])
  • 确定边界。 f [ i ] [ j ] = − ∞ , f [ 0 ] [ 0 ] = 0 f[i][j]=-∞,f[0][0]=0 f[i][j]=,f[0][0]=0,含义与01背包问题一致。
  • 确定目标解。 m a x ( f [ n ] [ j ] ) ( 0 < = j < = m ∣ j ) max(f[n][j])(0<=j<=m|j) max(f[n][j])(0<=j<=mj),含义与01背包问题一致。

【实战训练4-2-2】疯狂采药(题目来源:洛谷P1616)

疯狂的采药

题目背景

此题为纪念 LiYuxiang 而生。

题目描述

LiYuxiang 是个天资聪颖的孩子,他的梦想是成为世界上最伟大的医师。为此,他想拜附近最有威望的医师为师。医师为了判断他的资质,给他出了一个难题。医师把他带到一个到处都是草药的山洞里对他说:“孩子,这个山洞里有一些不同种类的草药,采每一种都需要一些时间,每一种也有它自身的价值。我会给你一段时间,在这段时间里,你可以采到一些草药。如果你是一个聪明的孩子,你应该可以让采到的草药的总价值最大。”

如果你是 LiYuxiang,你能完成这个任务吗?

此题和原题的不同点:

1 1 1. 每种草药可以无限制地疯狂采摘。

2 2 2. 药的种类眼花缭乱,采药时间好长好长啊!师傅等得菊花都谢了!

输入格式

输入第一行有两个整数,分别代表总共能够用来采药的时间 t t t 和代表山洞里的草药的数目 m m m

2 2 2 到第 ( m + 1 ) (m + 1) (m+1) 行,每行两个整数,第 ( i + 1 ) (i + 1) (i+1) 行的整数 a i , b i a_i, b_i ai,bi 分别表示采摘第 i i i 种草药的时间和该草药的价值。

输出格式

输出一行,这一行只包含一个整数,表示在规定的时间内,可以采到的草药的最大总价值。

样例 #1

样例输入 #1

70 3
71 100
69 1
1 2

样例输出 #1

140

提示

数据规模与约定
  • 对于 30 % 30\% 30% 的数据,保证 m ≤ 1 0 3 m \le 10^3 m103
  • 对于 100 % 100\% 100% 的数据,保证 1 ≤ m ≤ 1 0 4 1 \leq m \le 10^4 1m104 1 ≤ t ≤ 1 0 7 1 \leq t \leq 10^7 1t107,且 1 ≤ m × t ≤ 1 0 7 1 \leq m \times t \leq 10^7 1m×t107 1 ≤ a i , b i ≤ 1 0 4 1 \leq a_i, b_i \leq 10^4 1ai,bi104

【问题分析】本问题为完全背包模板题,问题分析如上,但上述算法的空间复杂度为 O ( n ∗ m ) O(n*m) O(nm),时间复杂度为 O ( n ∗ m 2 ) O(n*m^2) O(nm2)提交时可通过30%的数据。下面给出上述算法的实现代码,再对其进行优化。具体代码如下:

#include<bits/stdc++.h>
using namespace std;
long long  f[1005][1005],w[1005],v[1005];
long long max(long long a,long long b){
	return a>b?a:b;
}
int main()
{
	

	int m,n;
	cin>>m>>n;
	for(int i=1;i<=n;i++){
		cin>>w[i]>>v[i];
	}
	f[0][0]=0;
	for(int i=1;i<=n;i++){
		for(int j=0;j<=m;j++){
			for(int k=0;k<=j/w[i];k++){
				f[i][j]=max(f[i][j],f[i-1][j-k*w[i]]+k*v[i]);
			}
		}
	}
	cout<<f[n][m];
	return 0;
}

(2)完全背包的优化

降维优化代码

#include<bits/stdc++.h>
using namespace std;
long long  f[10000005],w[10005],v[10005];
int main()
{
	int m,n;
	cin>>m>>n;
	for(int i=1;i<=n;i++){
		cin>>w[i]>>v[i];
	}
	for(int i=1;i<=n;i++){
		for(int j=w[i];j<=m;j++){
			f[j]=max(f[j],f[j-w[i]]+v[i]);
		}
	}
	cout<<f[m];
	return 0;
}
3.多重背包

(1)多重背包的模型

多重背包的模型为:给定n种物品,其中第i种物品的体积为 w i w_i wi,价值为 v i v_i vi,每种物品有 C i C_i Ci件。有一体积为m的背包,请选择一些物品放入背包,使得在物品总体积不超过m的前提下,物品的价值总和最大。

  • 阶段划分。与01背包一样,多重背包仍然按已处理物品种数划分阶段,设阶段变量i代表前i种物品。
  • 设计状态。设 f [ i ] [ j ] f[i][j] f[i][j]代表从前i种物品选择若干件恰好放入体积为j的背包的最大价值和(体积j必须装满)。
  • 确定决策。对于状态 f [ i ] [ j ] f[i][j] f[i][j],问题进入第i阶段,决策对象为第i种物品,做出的决策即第i种物品选几件,可以为0件,1件,2件……min(c[i],j/w[i])件。设决策变量为k,那么决策变量k的范围为 0 < = k < = m i n ( c [ i ] , j / w [ i ] ) 0<=k<=min(c[i],j/w[i]) 0<=k<=min(c[i],j/w[i])
  • 确定状态转移方程。 f [ i ] [ j ] = m a x ( f [ i ] [ j ] , f [ i − 1 ] [ j − k ∗ w [ i ] ] + k ∗ v [ i ] ) f[i][j]=max(f[i][j],f[i-1][j-k*w[i]]+k*v[i]) f[i][j]=max(f[i][j],f[i1][jkw[i]]+kv[i]),其中k的取值范围为 ( 0 < = k < = m i n ( c [ i ] , j / w [ i ] ) ) (0<=k<=min(c[i],j/w[i])) (0<=k<=min(c[i],j/w[i]))
  • 确定边界。 f [ i ] [ j ] = − ∞ , f [ 0 ] [ 0 ] = 0 f[i][j]=-∞,f[0][0]=0 f[i][j]=,f[0][0]=0,含义与01背包问题一致。
  • 确定目标解。 m a x ( f [ n ] [ j ] ) max(f[n][j]) max(f[n][j]),其中j的取值范围为 0 < = j < = m 0<=j<=m 0<=j<=m,含义与01背包问题一致。

【实战训练4-2-3】宝物筛选(题目来源:洛谷P1776)

宝物筛选

题目描述

终于,破解了千年的难题。小 FF 找到了王室的宝物室,里面堆满了无数价值连城的宝物。

这下小 FF 可发财了,嘎嘎。但是这里的宝物实在是太多了,小 FF 的采集车似乎装不下那么多宝物。看来小 FF 只能含泪舍弃其中的一部分宝物了。

小 FF 对洞穴里的宝物进行了整理,他发现每样宝物都有一件或者多件。他粗略估算了下每样宝物的价值,之后开始了宝物筛选工作:小 FF 有一个最大载重为 W W W 的采集车,洞穴里总共有 n n n 种宝物,每种宝物的价值为 v i v_i vi,重量为 w i w_i wi,每种宝物有 m i m_i mi 件。小 FF 希望在采集车不超载的前提下,选择一些宝物装进采集车,使得它们的价值和最大。

输入格式

第一行为一个整数 n n n W W W,分别表示宝物种数和采集车的最大载重。

接下来 n n n 行每行三个整数 v i , w i , m i v_i,w_i,m_i vi,wi,mi

输出格式

输出仅一个整数,表示在采集车不超载的情况下收集的宝物的最大价值。

样例 #1

样例输入 #1

4 20
3 9 3
5 9 1
9 4 2
8 1 3

样例输出 #1

47

提示

对于 30 % 30\% 30% 的数据, n ≤ ∑ m i ≤ 1 0 4 n\leq \sum m_i\leq 10^4 nmi104 0 ≤ W ≤ 1 0 3 0\le W\leq 10^3 0W103

对于 100 % 100\% 100% 的数据, n ≤ ∑ m i ≤ 1 0 5 n\leq \sum m_i \leq 10^5 nmi105 0 ≤ W ≤ 4 × 1 0 4 0\le W\leq 4\times 10^4 0W4×104 1 ≤ n ≤ 100 1\leq n\le 100 1n100

【问题分析】多重背包模板题,空间复杂度为 O ( n ∗ m ) O(n*m) O(nm),时间复杂度为 O ( m ∗ Σ c [ i ] ) O(m*Σc[i]) O(mΣc[i])

具体代码如下:

#include<bits/stdc++.h>
using namespace std;
int f[105][1005],w[105],v[105],c[105];
int main()
{
	int n,m;
	cin>>n>>m;
	for(int i=1;i<=n;i++){
		cin>>v[i]>>w[i]>>c[i];
	}
	for(int i=1;i<=n;i++){
		for(int j=0;j<=m;j++){
			for(int k=0;k<=min(c[i],j/w[i]);k++){
				f[i][j]=max(f[i][j],f[i-1][j-k*w[i]]+k*v[i]);
			}
		}
	}
	cout<<f[n][m];
	return 0;
}

(2)多重背包的优化

在多重背包中,第i件物品有 c [ i ] c[i] c[i]件,如果我们将第i种物品拆分成 c [ i ] c[i] c[i]种新的物品,每种物品只有一件,问题就转化成了01背包问题。思考,为什么这样拆分是可行的?

二进制拆分优化

#include<bits/stdc++.h>
using namespace std;
int f[40005],w[105],v[105],c[105];
int w1[200005],v1[200005],n1;
void pre(int vv,int ww,int cc){
	int num=1;
	while(cc-num>0){
		n1++;
		w1[n1]=ww*num;
		v1[n1]=vv*num;
		cc-=num;
		num*=2;
	}
	n1++;
	w1[n1]=cc*ww;
	v1[n1]=vv*cc;
	
}
int main()
{
	int n,m;
	cin>>n>>m;
	for(int i=1;i<=n;i++){
		cin>>v[i]>>w[i]>>c[i];
		pre(v[i],w[i],c[i]);
	}
	for(int i=1;i<=n1;i++){
		for(int j=m;j>=w1[i];j--){
			f[j]=max(f[j],f[j-w1[i]]+v1[i]);	
		}
	}
	cout<<f[m];
	return 0;
}

(四)背包的衍生类型

背包的衍生类型主要有混合背包、分组背包、多维费用背包、依赖背包等,这一背包问题和基础背包问题的分析方法非常相似。背包问题千变万化,但万变不离其宗,请注意领悟背包问题的本质。

1.混合背包

混合背包的模型为:给定n种物品,其中第i种物品的体积为 w i w_i wi,价值为 v i v_i vi,某种物品只有一件,某种物品有无数件,某种物品有 c [ i ] c[i] c[i]件。有一体积为m的背包,请选择一些物品放入背包,使得在物品总体积不超过m的前提下,物品的价值总和最大。

混合背包的阶段划分、状态设计和01背包一样,区别在于对于状态 f [ i ] [ j ] f[i][j] f[i][j],需要讨论第i种物品是01、完全还是多重的,即针对物品的不同属性做不同的决策即可,这里不展开讨论。

2.分组背包

分组背包的模型为:给定n组物品,其中第i组有 c [ i ] c[i] c[i]件物品,第i组的第j件物品的体积为 w i j w_{ij} wij价值为 v i j v_{ij} vij。有一体积为m的背包,请选择一些物品放入背包,使得在每组至多选择一件且物品总体积不超过m的前提下,物品的价值总和最大。

(1)阶段划分。我们可按组数从前向后逐一考虑每个组内的物品,因此可按已处理的组数将问题求解过程划分为n个阶段,设阶段变量i表示前i组。

(2)设计状态。设 f [ i ] [ j ] f[i][j] f[i][j]代表从前i组物品种选择若干件恰好放入体积为j的背包的最大价值和。

(3)确定决策。对于状态 f [ i ] [ j ] f[i][j] f[i][j],问题进入第i阶段,决策对象为第i组物品,由于每组物品至多选1件,因此对第i组做出的决策为选0件,选1件,若是选1件,则需要枚举第i组的 c [ i ] c[i] c[i]件物品,考虑选哪一件即可。

(4)确定状态转移方程。若第i组物品选0件: f [ i ] [ j ] = f [ i − 1 ] [ j ] f[i][j]=f[i-1][j] f[i][j]=f[i1][j];若第i组物品选1件,假设为第k件: f [ i ] [ j ] = f [ i − 1 ] [ j − w [ i ] [ k ] ] + v [ i ] [ k ] ( j > = w [ i ] [ k ] ) f[i][j]=f[i-1][j-w[i][k]]+v[i][k](j>=w[i][k]) f[i][j]=f[i1][jw[i][k]]+v[i][k](j>=w[i][k])

(5)

这里不展开讨论。

2.分组背包

分组背包的模型为:给定n组物品,其中第i组有 c [ i ] c[i] c[i]件物品,第i组的第j件物品的体积为 w i j w_{ij} wij价值为 v i j v_{ij} vij。有一体积为m的背包,请选择一些物品放入背包,使得在每组至多选择一件且物品总体积不超过m的前提下,物品的价值总和最大。

(1)阶段划分。我们可按组数从前向后逐一考虑每个组内的物品,因此可按已处理的组数将问题求解过程划分为n个阶段,设阶段变量i表示前i组。

(2)设计状态。设 f [ i ] [ j ] f[i][j] f[i][j]代表从前i组物品种选择若干件恰好放入体积为j的背包的最大价值和。

(3)确定决策。对于状态 f [ i ] [ j ] f[i][j] f[i][j],问题进入第i阶段,决策对象为第i组物品,由于每组物品至多选1件,因此对第i组做出的决策为选0件,选1件,若是选1件,则需要枚举第i组的 c [ i ] c[i] c[i]件物品,考虑选哪一件即可。

(4)确定状态转移方程。若第i组物品选0件: f [ i ] [ j ] = f [ i − 1 ] [ j ] f[i][j]=f[i-1][j] f[i][j]=f[i1][j];若第i组物品选1件,假设为第k件: f [ i ] [ j ] = f [ i − 1 ] [ j − w [ i ] [ k ] ] + v [ i ] [ k ] ( j > = w [ i ] [ k ] ) f[i][j]=f[i-1][j-w[i][k]]+v[i][k](j>=w[i][k]) f[i][j]=f[i1][jw[i][k]]+v[i][k](j>=w[i][k])

(5)

(5)确定目标解。 m a x ( f [ n ] [ j ] ) ( 0 < = j < = m ) max(f[n][j])(0<=j<=m) max(f[n][j])(0<=j<=m)

  • 18
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小芒果_01

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值