经典的01背包问题:
有 N 件物品和一个容量是 V 的背包。每件物品只能使用一次。第 i 件物品的体积是vi,价值是 wi。求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。
输出最大价值。
输入格式,第一行两个整数,N,V,用空格隔开,分别表示物品数量和背包容积。接下来有 N行,每行两个整数 vi,wi,用空格隔开,分别表示第 i 件物品的体积和价值。
输出格式,输出一个整数,表示最大价值。
数据范围0<N,V≤1000 0<vi,wi≤1000
输入样例:
4 5
1 2
2 4
3 4
4 5
输出样例:
8
使用一个二维数组dp[i][j],表示的是选取第i个物品不超过j容积的最大价值,在遍历第i个物品时,dp[i][j]==max(前一个的value,前一个value加上本次的value),前提是加上了本次的value,不超过j容积的。可以列出公式。
#include<bits/stdc++.h>
#define LL long long
#define PP pair<int, int>
using namespace std;
const int N=1010;
int v[N],w[N]; //v是各背包容积,w是价值
int f[N][N]; //DP数组
int main() {
ios::sync_with_stdio(0);
int n,m;
cin>>n>>m; //有n个背包,要求不超过m的容积
for(int i=1;i<=n;i++) cin>>v[i]>>w[i];
for(int i=1;i<=n;i++){ //遍历背包
for(int j=0;j<=m;j++){ //遍历容积
f[i][j]=f[i-1][j]; //满足为j容积时可以放的最大价值
if(j>=v[i]) f[i][j]=max(f[i-1][j],f[i-1][j-v[i]]+w[i]);
}
}
cout<<f[n][m]<<endl; //输出为m容积可以发的最大价值
cin.tie(0);
cout.tie(0);
return 0;
}
用滚动数组来优化,从后往前遍历背包容积,同时确保j>=v[i],才可以加入value值,为什么是逆序呢,因为要更新第j位置就会需要j-v[i],再更新j-v[i]就需要j,导致j是已经是更新的了,这是错误的
//滚动数组优化版本
#include<iostream>
using namespace std;
const int N = 1010;
int n, m;
int v[N], w[N];
int f[N];
int main() {
cin >> n >> m;
for(int i = 1; i <= n; i++) cin >> v[i] >> w[i];
for(int i = 1; i <= n; i++) {
for(int j = m; j >= v[i]; j--) { //从后往前遍历j要求不超过其容积
f[j]=max(f[j],f[j-v[i]]+w[i]);
}
}
cout << f[m] << endl;
return 0;
}
数字组合背包问题(分组背包,有依赖的背包)
给定 N 个正整数 A1,A2,…,AN,从中选出若干个数,使它们的和为 M,求有多少种选择方案。
输入格式第一行包含两个整数 N和 M。第二行包含 N个整数,表示 A1,A2,…,AN。输出格式包含一个整数,表示可选方案数。
数据范围1≤N≤100 1≤M≤10000 1≤Ai≤1000
答案保证在 int 范围内。
输入样例:
4 4
1 1 2 2
输出样例:
3
用数组dp[j],表示和为j的选择方案。f(i,j)表示第i个整数时不超过j的选取方案,当遍历第i个整数时,选取第i个整数时为f(i-1,j-a[i]) ,不选第i个整数为f(i-1,j),最后加上f(i,j)
#include<bits/stdc++.h>
using namespace std;
const int n=101;
int a[n];
int f[10001];
int main(){
int N,M;
cin>>N>>M;
for(int i=1;i<=N;i++) cin>>a[i];
f[0]=1; //选取第0个元素只有一种选择方案
for(int i=1;i<=N;i++){
for(int j=M;j>=a[i];--j){ //满足a[i]不超过j
f[j]+=f[j-a[i]];
}
}
cout<<f[M]<<endl;
return 0;
}
开心的金明
金明今天很开心,家里购置的新房就要领钥匙了,新房里有一间他自己专用的很宽敞的房间。
更让他高兴的是,妈妈昨天对他说:“你的房间需要购买哪些物品,怎么布置,你说了算,只要不超过 N 元钱就行”。
今天一早金明就开始做预算,但是他想买的东西太多了,肯定会超过妈妈限定的N 元。
于是,他把每件物品规定了一个重要度,分为 5 等:用整数 1∼5 表示,第 5 等最重要。
他还从因特网上查到了每件物品的价格(都是整数元)。
他希望在不超过 N 元(可以等于 N 元)的前提下,使每件物品的价格与重要度的乘积的总和最大。 设第 j件物品的价格为v[j],重要度为 w[j],共选中了 k 件物品,编号依次为 j1,j2,…,jk,则所求的总和为: v[j1]×w[j1]+v[j2]×w[j2]+…+v[jk]×w[jk]
请你帮助金明设计一个满足要求的购物单。输入格式:输入文件的第 1 行,为两个正整数 N 和 m,用一个空格隔开。(其中 N 表示总钱数,m 为希望购买物品的个数) 从第 2 行到第 m+1 行,第 jj 行给出了编号为j−1 的物品的基本数据,每行有 2 个非负整数 v和 p。(其中 v表示该物品的价格,p 表示该物品的重要度)
输出格式:输出文件只有一个正整数,为不超过总钱数的物品的价格与重要度乘积的总和的最大值(数据保证结果不超过 108)。
数据范围1≤N<30000
1≤m<25
0≤v≤10000
1≤p≤5
输入样例:
1000 5
800 2
400 5
300 5
400 3
200 2
输出样例:
3900
典型的01背包问题,不同的是f[j]表示的是价格和重要度的乘奇。
#include<iostream>
using namespace std;
const int n=30010;
int v[n],w[n]; //v价格 w等级
int f[n];
int main(){
int N,m;
cin>>N>>m; //N总价格,m个数
for(int i=1;i<=m;i++) cin>>v[i]>>w[i];
for(int i=1;i<=m;i++){
int temp=w[i]*v[i];
for(int j=N;j>=v[i];j--){
f[j]=max(f[j],f[j-v[i]]+temp);
}
}
cout<<f[N]<<endl;
}
金明的预算
金明今天很开心,家里购置的新房就要领钥匙了,新房里有一间金明自己专用的很宽敞的房间。更让他高兴的是,妈妈昨天对他说:“你的房间需要购买哪些物品,怎么布置,你说了算,只要不超过 nn 元钱就行”。今天一早,金明就开始做预算了,他把想买的物品分为两类:主件与附件,附件是从属于某个主件的,下表就是一些主件与附件的例子:
主件 | 附件 |
---|---|
电脑 | 打印机,扫描仪 |
书柜 | 图书 |
书桌 | 台灯,文具 |
工作椅 | 无 |
如果要买归类为附件的物品,必须先买该附件所属的主件。每个主件可以有 0 个、1 个或 2 个附件。每个附件对应一个主件,附件不再有从属于自己的附件。金明想买的东西很多,肯定会超过妈妈限定的 n元。于是,他把每件物品规定了一个重要度,分为 5 等:用整数1∼5 表示,第 5 等最重要。他还从因特网上查到了每件物品的价格(都是 10 元的整数倍)。他希望在不超过 n 元的前提下,使每件物品的价格与重要度的乘积的总和最大。
设第 j 件物品的价格为 vj,重要度为wj,共选中了 k 件物品,编号依次为 j1,j2,jk,则所求的总和为:vj*wj 求和(每个k加起来)
请你帮助金明设计一个满足要求的购物单。
输入格式
第一行有两个整数,分别表示总钱数 n 和希望购买的物品个数 m。
第 2到第 (m+1) 行,每行三个整数,第 (i+1) 行的整数vi,pi,qi 分别表示第 i 件物品的价格、重要度以及它对应的的主件。如果 qi=0,表示该物品本身是主件。
输入:
1000 5 800 2 0 400 5 1 300 5 1 400 3 0 500 2 0
输出:
2200
这道背包问题有主件和附件之分,在确定要不要选取该物品时,存在判断。把每种情况都考虑到即可。1、都是主件。2、主件和附件1。3、主件和附件2。4、主件和附件1和附件2。每次输入进行判断,保存该主件的附件个数,以及对于的v和v*p
#include<bits/stdc++.h>
#define LL long long
#define PP pair<int, int>
using namespace std;
const int N=32001;
int mw[33333],mv[33333],fw[33333][3],fv[33333][3],f[33333],v,p,q;
//mw是价格 mv是乘奇
int main(){
int n,m;
cin>>n>>m;
for(int i=1;i<=m;i++){
cin>>v>>p>>q;
if(!q){ //主件
mw[i]=v;
mv[i]=v*p;
}
else{
fw[q][0]++; //记录主件q的附件个数
fw[q][fw[q][0]]=v;
fv[q][fw[q][0]]=v*p;
}
}
for(int i=1;i<=m;i++){
for(int j=n;j>=mw[i];j--){
f[j]=max(f[j],f[j-mw[i]]+mv[i]); //只要主件
if(j>=mw[i]+fw[i][1]) //要主件和附件1
f[j]=max(f[j],f[j-mw[i]-fw[i][1]]+mv[i]+fv[i][1]);
if(j>=mw[i]+fw[i][2]) //要主件和附件2
f[j]=max(f[j],f[j-mw[i]-fw[i][2]]+mv[i]+fv[i][2]);
if(j>=mw[i]+fw[i][1]+fw[i][2]) //要主件和附件1和2
f[j]=max(f[j],f[j-mw[i]-fw[i][1]-fw[i][2]]+mv[i]+fv[i][1]+fv[i][2]);
}
}
cout<<f[n]<<endl;
}