动态规划——01背包问题-创新互联
给定一个有一定容量的背包,和n个物品,每个物品只能选择一次。
成都创新互联拥有10多年成都网站建设工作经验,为各大企业提供成都网站建设、成都网站制作服务,对于网页设计、PC网站建设(电脑版网站建设)、手机APP定制开发、wap网站建设(手机版网站建设)、程序开发、网站优化(SEO优化)、微网站、域名注册等,凭借多年来在互联网的打拼,我们在互联网网站建设行业积累了很多网站制作、网站设计、网络营销经验,集策划、开发、设计、营销、管理等网站化运作于一体,具备承接各种规模类型的网站建设项目的能力。每个物品有其对应的体积和价值。
问背包最多能装下的物品的大价值为多少。
输入格式:
第一行两个整数,N,V,分别表示物品数量和背包容积。
接下来有 N 行,每行两个整数 vi,wi 用空格隔开,分别表示第 i 件物品的体积和价值。
输出格式:
输出一个整数,表示大价值。
思路:假设f[i][j]代表当只选择前n个物品并且背包容量只有j的时候所能装下物品的大价值
f[i][j] = max(f[i - 1][j],f[i - 1][j - v[j]] + w[j])
题目的答案就是f[n][m]。
为什么这样做是正确的呢:
当我们在面对第i件物品的时候,我们一定会在选择第i件物品和不选择第i件物品这两种情况中做出选择。
集合的角度:
假设我们有一个集合,集合表示的是只看前i件物品,背包容量为j的时候的所有物品选法,f[i][j]取集合中的大值,该集合可以划分为选择第i件物品和不选择第i件物品两个集合,f[i][j]取两个集合中的大值即可。
选和不选两种情况下状态的改变:
如果我们不选择第i件物品,那么我们的背包的价值就不变,等于f[i-1][j]。
如果我们背包的容量足够,我们选择第i件物品的话,虽然我们得到了第i件物品的价值,但是我们背包的体积一定会损耗,体积如果损耗,那么我们就有可能放下前面已经装进背包中的物品,此时我们的背包价值是不一定增大的。
所以我们的f[i][j]取两者的大值,也就是
f[i][j] = max(f[i - 1][j],f[i - 1][j - v[j]] + w[j])
给出图示:
代码如下:
#includeusing namespace std;
const int N = 1010;
int v[N], w[N];//每个物品的体积和价值
int f[N][N];//每个状态的大价值
int n, V;//物品数和背包容量
int main() {
cin >>n >>V;
for (int i = 1; i<= n; i++)
cin >>v[i] >>w[i];
for (int i = 1; i<= n; i++)
for (int j = 0; j<= V; j++) {
f[i][j] = f[i - 1][j];
if (j >= v[i])f[i][j] = max(f[i][j], f[i - 1][j - v[i]] + w[i]);
}
cout<< f[n][V];
return 0;
}
优化至一维:我们发现,我们在更新的时候只会用到两层状态,每一个状态都是由上一层左边的状态推导而来的,我们不妨将这两行合并。
dp方程为:
f[j] = max(f[j], f[j - v] + w)
一维状态中,f[j]默认就是上一层的f[j],所以不用进行不选第i个物品的更新。当我们选择第i个物品时当,我们需要用到上一层左边的状态,所以我们不能把本层左边的状态更新成上一层左边的状态。如果我们从前往后进行更新,那么上一层的左边的状态就被更新掉了。所以我们应该从后往前更新。
代码如下:
#includeusing namespace std;
const int N = 1010;
int n, V;//物品数
int v, w;//总体积
int f[N];//状态
int main() {
cin >>n >>V;
for (int i = 0; i< n; i++) {
cin >>v >>w;
for (int j = V; j >= v; j--)
if (v<= j)f[j] = max(f[j - v] + w, f[j]);//f[j]默认就是上一行的值,所以不用更新v小于j的情况
}
cout<< f[V];
return 0;
}
注意:
1.背包问题中的状态,无论是一维还是二维,dp数组要同时满足背包体积和物品数量两个边界,而不是满足数量边界即可。
2.状态更新要用到f[i-1],所以物品下标要从1开始。
例题: 装箱问题:有一个箱子容量为 V,同时有 n 个物品,每个物品有一个体积(正整数)。
在 n 个物品中,任选若干个装入箱内,使箱子的剩余空间为最小。
输入格式:
第一行是一个整数 V,表示箱子容量(0≤V小于等于20000)。
第二行是一个整数 n,表示物品数(0≤n≤30)。
接下来 n 行,每行一个正整数(不超过10000),分别表示这 n 个物品的各自体积。
思路:
题目要尽可能的装满箱子。我们给每个物品赋予一个价属性,价值的值就是体积的值。所以就是求一定体积下的大价值是多少。
代码如下:
//二维
#includeusing namespace std;
const int N = 20010, M = 40;
int f[M][N];
int n, m;//物品数和背包容量
int main() {
cin >>m >>n;
int v, w;
for (int i = 1; i<= n; i++) {//前i个物品
//读入体积和价值
cin >>v;
w = v;
for (int j = 0; j<= m; j++) {//体积为j
f[i][j] = f[i - 1][j];//不选该物品
if (j >= v)f[i][j] = max(f[i][j], f[i - 1][j - v] + w);
}
}
cout<< m - f[n][m];//记得做减法
return 0;
}
//一维
#includeusing namespace std;
const int N = 20010;
int f[N];
int n, m;//物品数和背包容量
int main() {
cin >>m >>n;
int v, w;
for (int i = 1; i<= n; i++) {//前i个物品
//读入体积和价值
cin >>v;
w = v;
for (int j = m; j >= v; j--) {//从后往前更新
f[j] = max(f[j], f[j - v] + w);
}
}
cout<< m - f[m];//记得做减法
return 0;
}
注意:最后输出的是剩余多少空间,我们求出来的是大体积,所以不要忘记做减法。
数字组合:给定 N 个正整数,从中选出若干个数,使它们的和为 M,求有多少种选择方案。
输入格式
第一行包含两个整数 N 和 M(1≤N≤100,1≤M≤10000)。
第二行包含 N 个整数,表示 A1,A2,…,AN。
输出格式:
包含一个整数,表示可选方案数。
思路:
f[i][j]表示只看前i个物品,和为j的情况。
如果选择第i个物品:f[i][j] = f[i-1][j-v[i]]。
如果不选第i个物品:f[i][j] = f[i-1][j]。
f[i][j]等于两者之和。
该题需要初始化,f[0][0] = 1。
代码如下:
//二维
#includeusing namespace std;
const int N = 110, M = 10010;
int f[N][M];
int n, m;//n个数和为m
int main() {
cin >>n >>m;
int v;
f[0][0] = 1;
for (int i = 1; i<= n; i++) {//前i个数
cin >>v;
for (int j = 0; j<= m; j++) {
f[i][j] += f[i - 1][j];//不选
if (j >= v)f[i][j] += f[i - 1][j - v];//选
}
}
cout<< f[n][m];
return 0;
}
//一维
#includeusing namespace std;
const int M = 10010;
int f[M];
int n, m;//n个数和为m
int main() {
cin >>n >>m;
int v;
f[0] = 1;//初始化
for (int i = 1; i<= n; i++) {//前i个数
cin >>v;
for (int j = m; j >= v; j--)
f[j] += f[j - v];//选
}
cout<< f[m];
return 0;
}
你是否还在寻找稳定的海外服务器提供商?创新互联www.cdcxhl.cn海外机房具备T级流量清洗系统配攻击溯源,准确流量调度确保服务器高可用性,企业级服务器适合批量采购,新人活动首月15元起,快前往官网查看详情吧
新闻名称:动态规划——01背包问题-创新互联
本文路径:http://scjbc.cn/article/ipjoi.html