变种 背包问题_01背包及其变种(物品无限背包、恰好装满背包)
一、01背包问题
01背包是在M件物品取出若干件放在空间为W的背包里,每件物品的体积为C1,C2,…,Cn,与之相对应的价值为W1,W2,…,Wn.求解将那些物品装入背包可使总价值最大。
动态规划:
1) 子问题定义:F[i][j]表示前i件物品中选取若干件物品放入剩余空间为j的背包中所能得到的最大价值。
2) 根据第i件物品放或不放进行决策
其中F[i-1][j]表示前i-1件物品中选取若干件物品放入剩余空间为j的背包中所能得到的最大价值;
而F[i-1][j-C[i]]+W[i]表示前i-1件物品中选取若干件物品放入剩余空间为j-C[i]的背包中所能取得的最大价值加上第i件物品的价值
根据第i件物品放或是不放确定遍历到第i件物品时的状态F[i][j]。
设物品件数为N,背包容量为V,第i件物品体积为C[i],第i件物品价值为W[i]。
由此写出伪代码如下:
1 F[0][] ← {0}2
3 F[][0] ← {0}4
5 for i←1to N6
7 do for k←1to V8
9 F[i][k] ← F[i-1][k]10
11 if(k >=C[i])12
13 then F[i][k] ← max(F[i][k],F[i-1][k-C[i]]+W[i])14
15 return F[N][V]
上述代码9-13行也可以这样写:
if(k
then F[i][k] ← F[i-1][k]
elseF[i][k] ← max(F[i-1][k],F[i-1][k-C[i]]+W[i])
理解为:当第i个物品的体积大于背包剩余容量时,第i个物品肯定不能被放进去,所以F[i][k]=F[i-1][k],
而当第i个物品的体积小于或等于背包剩余容量时,可以有两种选择,放第i个物品(F[i-1][k-C[i]]+W[i])或者不放(F[i-1][k]),然后选择两者之间最优的。
而写成第一种等价的伪代码是为了更好的理解从二维数组解法到一维数组解法的转换。
根据算法求出的最大价值表本身其实含有位置信息,从F[N][V]逆着走向F[0][0],设i=N,j=V,如果F[i][j]==F[i-1][j-C[i]]+W[i]说明包里面有第i件物品,同时j -= C[i],不管F[i][j]与F[i-1][j-C[i]]+W[i]相不相等i都要减1,因为01背包的第i件物品要么放要么不放,不管放还是不放其已经遍历过了,需要继续往下遍历。
打印背包内物品的伪代码如下:
1 i←N2
3 j←V4
5 while(i>0 && j>0)6
7 do if(F[i][j]=F[i-1][j-C[i]]+W[i])8
9 then Print W[i]10
11 j←j-C[i]12
13 i←i-1
也可以定义一个二维数组Path[N][V]来存放背包内物品信息,开始时Path[N][V]初始化为0,当 F[i][j]==F[i-1][j-C[i]]+W[i]时Path[i][j]置1。最后通过从Path[N+1][V+1]逆着走向Path[0][0]来获取背包内物品。其中Path[0][]与Path[][0]为边界。
加入路径信息的伪代码如下:
F[0][] ← {0}
F[][0] ← {0}
Path[][] ←0
for i←1to Ndo for k←1to V
F[i][k] ← F[i-1][k]if(k >= C[i] && F[i][k] < F[i-1][k-C[i]]+W[i])
then F[i][k] ← F[i-1][k-C[i]]+W[i]
Path[i][k] ←1
return F[N][V] and Path[][]
打印背包内物品的伪代码如下:
i←N
j←V
while(i>0 && j>0)
do if(Path[i][j] = 1)
then Print W[i]
j←j-C[i]
i←i-1
将使用二位数组改为使用一维数组:
观察伪代码可也发现,F[i][j]只与F[i-1][j]和F[i-1][j-C[i]]有关,即只和i-1时刻状态有关,所以我们只需要用一维数组F[]来保存i-1时的状态F[]。假设i-1时刻的F[]为{a0,a1,a2,…,av},难么i时刻的F[]中第k个应该为max(ak,ak-C[i]+W[i])即max(F[k],F[k-C[i]]+W[i]),这就需要我们遍历V时逆序遍历,这样才能保证求i时刻F[k]时F[k-C[i]]是i-1时刻的值。如果正序遍历则当求F[k]时其前面的F[0],F[1],…,F[K-1]都已经改变过,里面存的都不是i-1时刻的值,这样求F[k]时利用F[K-C[i]]必定是错的值。最后F[V]即为最大价值。
求F[j]的状态方程如下:
伪代码如下:
1 F[] ← {0}2
3 for i ← 1to N4
5 do fork ← V to C[i]6
7 F[k] ← max(F[k],F[k-C[i]]+W[i])8
9 return F[V]
加入路径信息的伪代码如下:
1 F[] ← {0}2
3 Path[][]←0
4
5 for i←1to N6
7 do fork←V to C[i]8
9 if(F[k] < F[k-C[i]]+W[i])10
11 then F[k] ← F[k-C[i]]+W[i]12
13 Path[i][k] ← 1
14
15 return F[V] and Path[][]
二、物品无限的背包问题
将01背包一维数组解法中j的遍历顺序do for k←V to C[i]改为do for k←C[i] to V就变成了物品无限背包的解法。
1 F[] ← {0}2
3 for i ← 1to N4
5 do fork ← C[i] to V6
7 F[k] ← max(F[k],F[k-C[i]]+W[i])8
9 return F[V]
三、完全背包(要求背包必须装满,而不是最大限度的装)
主要是在01背包的初始化过程中的不同,然后考虑第i个物体的时候判断下是否可以装满的条件
1 F[][] ← {-1}2
3 F[][0] ← {0}4
5 for i←1to N6
7 do for k←1to V8 if (F[i-1][k-C[i]]!=-1)9 then10
11 F[i][k] ← F[i-1][k]12
13 if(k >=C[i])14
15 then F[i][k] ← max(F[i][k],F[i-1][k-C[i]]+W[i])16
17 return F[N][V]
四、代码实际操练:
二维数组解法:
01背包问题具体例子:先输入两个数n,V表示物品的个数和背包的容量,接下来输入n组数据代表n种物品,每组数据有两个值对应物品的体积和价值,每种物品只有一个,求在背包容量下能装物品最大价值,并求出最大价值下,组合中各个物品的价值?
#include
using namespacestd;const int N_max=100;//物品个数上限
const int V_max=100;//背包容量上限
intdp[N_max][V_max];boolflag[N_max][V_max];intmain()
{int n;//实际输入物品的个数
cin>>n;int V;//背包的实际容量
cin>>V;int *v=new int[n+1];int *price=new int[n+1];//输入n组数据,分别为体积v和价值price//注意这里要从1开始
for(int i=1;i<=n;i++)
{
cin>>v[i]>>price[i];
}//初始化dp数组//注意这里的=
for(int i=0;i<=n;i++)
{
dp[i][0]=0;
}for(int i=0;i<=V;i++)
{
dp[0][i]=0;
}//初始化flag数组
memset(flag,false,(n+1)*(V+1)*sizeof(bool));//开始递推
for(int i=1;i<=n;i++)
{for(int j=1;j<=V;j++)
{
dp[i][j]=dp[i-1][j];if(v[i]<=j && dp[i-1][j-v[i]]+price[i]>dp[i][j])//放下该物品
{
dp[i][j]=dp[i-1][j-v[i]]+price[i];
flag[i][j]=true;
}
}
}
cout<
cout<=0 && j>=0)
{if(flag[i][j])
{
cout<
j=j-v[i];
}
i--;
}return 0;
}
一维数组的解法:
#include
using namespacestd;bool flag[100][100]={false};intmain()
{int n;//实际输入物品的个数
cin>>n;int V;//背包的实际容量
cin>>V;int *dp=new int[V+1];int *v=new int[n+1];int *price=new int[n+1];//输入n组数据,分别为体积v和价值price//注意这里要从1开始
for(int i=1;i<=n;i++)
{
cin>>v[i]>>price[i];
}//初始化dp数组
memset(dp,0,(V+1)*sizeof(int));//开始递推
for(int i=1;i<=n;i++)
{for(int j=V;j>=v[i];j--)//for(int j=v[i];j<=V;j++)
{if( dp[j-v[i]]+price[i]>dp[j])//放下该物品
{
dp[j]=dp[j-v[i]]+price[i];
flag[i][j]=true;
}
}
}
cout<
cout<=0 && j>=0)
{if(flag[i][j])
{
cout<
j=j-v[i];
}
i--;
}return 0;
}
调试结果:
物品无限背包问题具体例子:先输入两个数n,V表示物品的个数和背包的容量,接下来输入n组数据代表n种物品,每组数据有两个值对应物品的体积和价值,每种物品有无限个,求在背包容量下能装物品最大价值,并求出最大价值下,组合中各个物品的价值?
#include#include#include
using namespacestd;bool flag[100][100]={false};intmain()
{int n;//实际输入物品的个数
cin>>n;int V;//背包的实际容量
cin>>V;int *dp=new int[V+1];int *v=new int[n+1];int *price=new int[n+1];//输入n组数据,分别为体积v和价值price//注意这里要从1开始
for(int i=1;i<=n;i++)
{
cin>>v[i]>>price[i];
}//初始化dp数组
memset(dp,0,(V+1)*sizeof(int));//开始递推
for(int i=1;i<=n;i++)
{for(int j=v[i];j<=V;j++)
{if( dp[j-v[i]]+price[i]>dp[j])//放下该物品
{
dp[j]=dp[j-v[i]]+price[i];
flag[i][j]=true;
}
}
}
cout<
}
01背包下恰好装满的例子:先输入两个数n,V表示物品的个数和背包的容量,接下来输入n组数据代表n种物品,每组数据有两个值对应物品的体积和价值,每种物品只有一个,求在背包恰好装满时物品最大价值,并求出最大价值下,组合中各个物品的价值,若无法恰好装满,则输出无法装满的提示语句?
#include
using namespacestd;const int N_max=100;//物品个数上限
const int V_max=100;//背包容量上限
intdp[N_max][V_max];boolflag[N_max][V_max];intmain()
{int n;//实际输入物品的个数
cin>>n;int V;//背包的实际容量
cin>>V;int *v=new int[n+1];int *price=new int[n+1];//输入n组数据,分别为体积v和价值price//注意这里要从1开始
for(int i=1;i<=n;i++)
{
cin>>v[i]>>price[i];
}//注意恰好装满与普通01背包的初始化条件是不同的
memset(dp,-1,sizeof(dp));for(int i=0;i<=n;i++)
{
dp[i][0]=0;
}//初始化flag数组
memset(flag,false,(n+1)*(V+1)*sizeof(bool));//开始递推
for(int i=1;i<=n;i++)
{for(int j=1;j<=V;j++)
{//注意这里是恰好装满时的判断条件
if(dp[i-1][j-v[i]]!=-1)
{
dp[i][j]=dp[i-1][j];if(v[i]<=j && dp[i-1][j-v[i]]+price[i]>dp[i][j])//放下该物品
{
dp[i][j]=dp[i-1][j-v[i]]+price[i];
flag[i][j]=true;
}
}
}
}if(dp[n][V]==-1)
{
cout<
}else{
cout<
cout<=0 && j>=0)
{if(flag[i][j])
{
cout<
j=j-v[i];
}
i--;
}
}return 0;
}
变种 背包问题_01背包及其变种(物品无限背包、恰好装满背包)相关推荐
- 【动态规划笔记】动态规划初始化细节问题:恰好装满背包
求最优解的背包问题中,有的题目要求[恰好装满背包],有的题目并[没有要求]必须把背包装满 实现方法: 初始化有所不同 设:F[i]表示容量为i的背包能够装的最大价值 恰好装满背包: 初始化: F ...
- //假设有一个能装入总体积为T的背包和n件体积分别为w1,w2....wn.的物品,能否从n件物品中挑选若干件恰好装满背包,即使w1+w2+....+wn=T,要求找出所有满足上述条件的解。例如:当T
//背包问题 //假设有一个能装入总体积为T的背包和n件体积分别为w1,w2....wn.的物品,能否从n件物品中挑选若干件恰好装满背包,即使w1+w2+....+wn=T,要求找出所有满足上述条件的 ...
- 恰好装满背包、恰好取k倍(取余)
恰好装满背包(取余) 题目情形总结 判断整除 糖果 波动数列 题目情形总结 物品容量恰好等于m (恰好装满背包) 和 取得的糖果数恰好为k的倍数 若要求此时的最大价值,dp数组初始化为负无穷 详细解释 ...
- 01背包及其变种(物品无限背包、恰好装满背包)
一.01背包问题 01背包是在M件物品取出若干件放在空间为W的背包里,每件物品的体积为C1,C2,-,Cn,与之相对应的价值为W1,W2,-,Wn.求解将那些物品装入背包可使总价值最大. 动态规划: ...
- 动态规划 背包问题小结 0-1背包(采药 九度第101题) 完全背包(Piggy-Bank POJ 1384) 多重背包(珍惜现在,感恩生活 九度第103题)
本小结介绍0-1背包.完全背包以及多重背包问题 记忆要点: 0-1背包:二维数组情况下,顺序遍历体积或者倒序均可以 降维情况下需倒序遍历体积 完全背包:数组降维+顺序遍历 ...
- Python3使用动态规划处理背包问题:完全背包(含背包恰好装满的情况)
文章目录 题目介绍 题解1 题解2 题解3 完全背包问题是基于01背包的,如果对01背包问题不熟悉,可以参考: Python3使用动态规划处理01背包问题 题目介绍 原题链接:NC309 完全背包 描 ...
- 【动态规划】完全背包:存钱罐(恰好装满)
背包恰好装满问题: 设有n个物品,其重量(或占用空间)分别为w1, W.,...Wn.价值分别为V1,2....n. ← 给定一个总容量为W的背包,每个物品只能整个放入背包或不放.← 问:如何选择放入 ...
- 01背包的变形问题----背包恰好装满
01背包的变形问题----背包恰好装满 在看本文之前建议先看一下我之前发过的01背包详解. https://blog.csdn.net/Iseno_V/article/details/10000113 ...
- 变种 背包问题_【朝夕的ACM笔记】动态规划-背包问题
[朝夕的ACM笔记]目录与索引 背包问题 一.0/1背包 1.1 问题描述 有 件物品和一个大小为 的背包,以及若干物品,每种物品只有一件,大小分别为 ,其价值分别为 .问题:将哪些物品装入背包,可使 ...
最新文章
- AutoCAD.NET API 最新(2012)教程下载及在线视频教程DevTV 第8讲 用户界面
- java创建集合有的不用泛型_为什么在Java泛型右手边的集合类型没有任何影响?...
- CNN-4: GoogLeNet 卷积神经网络模型
- android点击地址调用地图,Android 实现点击按钮 调用手机外部地图导航
- java手机震动_Windows Phone 7 开发 之使手机震动
- 请确保此代码文件中定义的类与“inherits”属性匹配.并且该类扩展的基类(例如 Page 或 UserControl)是正确...
- 组态软件专用短信终端
- springboot中Word转PDF技巧
- k8s service nodeport
- 下载windows10.iso
- 虚拟机Hadoop localhost:8080无法打开解决办法
- 恶魔奶爸语法1-3课
- 负载平衡是什么意思_什么是负载平衡?为什么要使用它?
- 数据库MySQL总结(一)SQL语句
- RISC-V架构的开源处理器分析
- 信访积案化解和网上信访受理平台实践案例
- php打出等边三角形,CSS_CSS画出各种三角形如等边三角形,下面的代码将演示三角形如何 - phpStudy...
- 天大2021年秋学期考试《电工技术实验》离线作业考核试题
- CAD制图教程:什么是双跑楼梯?
- 将有length属性的对象转换成数组