一:01背包
有N件物品和一个容量为v的背包。第i件物品的费用(即体积)是w[i],价值是c[i],求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值和最大
状态转移方程:f[i][v]=max{f[i-1][v],f[i-1][v-w[i]]+c[i]}
一维数组:f[v]=max{f[v],f[v-w[i]]+c[i]}
1267:【例9.11】01背包问题
【题目描述】
一个旅行者有一个最多能装 MM 公斤的背包,现在有 nn 件物品,它们的重量分别是W1,W2,...,WnW1,W2,...,Wn,它们的价值分别为C1,C2,...,CnC1,C2,...,Cn,求旅行者能获得最大总价值。
【输入】
第一行:两个整数,MM(背包容量,M≤200M≤200)和NN(物品数量,N≤30N≤30);
第2..N+12..N+1行:每行二个整数Wi,CiWi,Ci,表示每个物品的重量和价值。
【输出】
仅一行,一个数,表示最大总价值。
【输入样例】
10 4
2 1
3 3
4 5
7 9
【输出样例】
12
解法一:
#include<bits/stdc++.h>
using namespace std;
const int N=205;
int m,n;
int dp[N][N];
int w[N],v[N];
int main()
{
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=m;j>=0;j--){
if(w[i]>j) dp[i][j]=dp[i-1][j];
else dp[i][j]=max(dp[i-1][j],dp[i-1][j-w[i]]+v[i]);
}
}
cout<<dp[n][m]<<endl;
return 0;
}
解法二:
#include<bits/stdc++.h>
using namespace std;
int dp[300];
int w[35],v[35];
int M,N;
int main()
{
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=M;j>w[i];j--)
dp[j]=max(dp[j],dp[j-w[i]]+v[i]);
}
cout<<dp[M]<<endl;
return 0;
}
二:完全背包
有N种物品和一个容量为V的背包,每种物品都有无限件可用。第i种物品的费用是w[i],价值是c[i]。求解将那些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大
其实就是01背包是逆序这个是正序,其他和01背包差不多
1268:【例9.12】完全背包问题
【题目描述】
设有n种物品,每种物品有一个重量及一个价值。但每种物品的数量是无限的,同时有一个背包,最大载重量为M,今从n种物品中选取若干件(同一种物品可以多次选取),使其重量的和小于等于M,而价值的和为最大。
【输入】
第一行:两个整数,M(背包容量,M≤200)和N(物品数量,N≤30);
第2..N+1行:每行二个整数Wi,Ci,表示每个物品的重量和价值。
【输出】
仅一行,一个数,表示最大总价值。
【输入样例】
10 4
2 1
3 3
4 5
7 9
【输出样例】
max=12
方法一:
#include<bits/stdc++.h>
using namespace std;
int M,N;
int f[35][250];
int w[34],c[35];
int main()
{
cin>>M>>N;
for(int i=1;i<=N;i++){
cin>>w[i]>>c[i];
}
for(int i=1;i<=N;i++){
for(int v=0;v<=M;v++){
if(v<w[i]) f[i][v]=f[i-1][v];
else f[i][v]=max(f[i-1][v],f[i][v-w[i]]+c[i]);
}
}
cout<<"max="<<f[N][M]<<endl;
return 0;
}
方法二:
#include<bits/stdc++.h>
using namespace std;
int M,N;
int f[250];
int w[34],c[35];
int main()
{
cin>>M>>N;
for(int i=1;i<=N;i++){
cin>>w[i]>>c[i];
}
for(int i=1;i<=N;i++){
for(int v=w[i];v<=M;v++){
f[v]=max(f[v],f[v-w[i]]+c[i]);
}
}
cout<<"max="<<f[M]<<endl;
return 0;
}
三:多重背包
有N种物品和一个容量为V的背包。第i种物品最多有n[i]件可用,每件费用是w[i],价值是c[i].求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大
1269:【例9.13】庆功会
【题目描述】
为了庆贺班级在校运动会上取得全校第一名成绩,班主任决定开一场庆功会,为此拨款购买奖品犒劳运动员。期望拨款金额能购买最大价值的奖品,可以补充他们的精力和体力。
【输入】
第一行二个数n(n≤500),m(m≤6000),其中n代表希望购买的奖品的种数,m表示拨款金额。
接下来n行,每行3个数,v、w、s,分别表示第I种奖品的价格、价值(价格与价值是不同的概念)和能购买的最大数量(买0件到s件均可),其中v≤100,w≤1000,s≤10。
【输出】
一行:一个数,表示此次购买能获得的最大的价值(注意!不是价格)。
【输入样例】
5 1000
80 20 4
40 50 9
30 50 7
40 30 6
20 20 1
【输出样例】
1040
方法一:
#include<bits/stdc++.h>
using namespace std;
int f[6500];
int v[6500],w[6500],s[6500];
int main()
{
int n,m;
cin>>n>>m;
for(int i=1;i<=n;i++){
cin>>v[i]>>w[i]>>s[i];
}
for(int i=1;i<=n;i++){
for(int j=m;j>=0;j--){
for(int k=0;k<=s[i];k++){
if(j<k*v[i]) break;
f[j]=max(f[j],f[j-k*v[i]]+k*w[i]);
}
}
}
cout<<f[m]<<endl;
return 0;
}
方法二:进行二进制优化,转换为01背包
#include<bits/stdc++.h>
using namespace std;
int n,m,n1=0;
int f[6550];
int v[100000],w[100000];
int main()
{
cin>>n>>m;
for(int i=1;i<=n;i++){
int x,y,s,t=1;
cin>>x>>y>>s;
while(s>=t){
v[++n1]=x*t;
w[n1]=y*t;
s-=t;
t*=2;
}
v[++n1]=x*s;
w[n1]=y*s;
}
for(int i=1;i<=n1;i++){
for(int j=m;j>=v[i];j--){
f[j]=max(f[j],f[j-v[i]]+w[i]);
}
}
cout<<f[m]<<endl;
return 0;
}
四:混合三种背包问题
如果将01背包,完全背包,多重背包混合起来,也就是说,有的物品只可以取一次(01),有的物品可以取无限次(完全背包),有的物品可以取得次数有一个上限(多重背包)
1270:【例9.14】混合背包
【题目描述】
一个旅行者有一个最多能装V公斤的背包,现在有n件物品,它们的重量分别是W1,W2,...,WnW1,W2,...,Wn,它们的价值分别为C1,C2,...,CnC1,C2,...,Cn。有的物品只可以取一次(01背包),有的物品可以取无限次(完全背包),有的物品可以取的次数有一个上限(多重背包)。求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大。
【输入】
第一行:二个整数,M(背包容量,M≤200),N(物品数量,N≤30);
第2..N+1行:每行三个整数Wi,Ci,PiWi,Ci,Pi,前两个整数分别表示每个物品的重量,价值,第三个整数若为0,则说明此物品可以购买无数件,若为其他数字,则为此物品可购买的最多件数(PiPi)。
【输出】
仅一行,一个数,表示最大总价值。
【输入样例】
10 3
2 1 0
3 3 1
4 5 4
【输出样例】
11
【提示】
选第一件物品1件和第三件物品2件。
#include<bits/stdc++.h>
using namespace std;
int m,n;
int w[35],c[35],p[35];
int f[250];
int main()
{
cin>>m>>n;
for(int i=1;i<=n;i++)
cin>>w[i]>>c[i]>>p[i];
for(int i=1;i<=n;i++){
if(p[i]==1){//01
for(int j=m;j>=w[i];j--)
f[j]=max(f[j],f[j-w[i]]+c[i]);
}
else if(p[i]==0){//完全
for(int j=w[i];j<=m;j++){
f[j]=max(f[j],f[j-w[i]]+c[i]);
}
}
else{//多重
for(int j=m;j>=0;j--){
for(int k=0;k<=p[i];k++){
if(j-k*w[i]>=0)
f[j]=max(f[j],f[j-k*w[i]]+k*c[i]);
}
}
}
}
cout<<f[m]<<endl;
return 0;
}
#include<bits/stdc++.h>
using namespace std;
int m,n;
int w[35],c[35],p[35];
int f[300];
int main()
{
cin>>m>>n;
for(int i=1;i<=n;i++){
cin>>w[i]>>c[i]>>p[i];
}
for(int i=1;i<=n;i++){
if(p[i]==0){//完全背包
for(int j=w[i];j<=m;j++){
f[j]=max(f[j],f[j-w[i]]+c[i]);
}
}
else{//01背包和多重背包
for(int j=1;j<=p[i];j++){
for(int k=m;k>=w[i];k--)
f[k]=max(f[k],f[k-w[i]]+c[i]);
}
}
}
cout<<f[m]<<endl;
return 0;
}
五:二维费用的背包问题
二维费用的背包问题是指:对于每件物品,具有俩种不用的费用;选择这件物品必须同时付出这俩种代价;对于每种代价都有一个可付出的最大值(背包容量)。问怎样选择物品可以得到最大的价值。设这俩种代价分别是代价1和代价2,第i件物品所需的俩种代价分别为a[i]和b[i]。俩种代可付出的最大值(俩种背包容量)分别为V和U.物品的价值为c[i];
算法:
费用加了一维,只需要状态也加一维即可,设f[i][v][u]表示前i件物品付出俩种代价分别为V和U时得到的最大值
状态转移方程就是:f[i][v][u]=max(f[i-1][v][u],f[i-1][v-a[i]][u-b[i]]+ c[i])
同理可以转换成二维数组求解
1271:【例9.15】潜水员
【题目描述】
潜水员为了潜水要使用特殊的装备。他有一个带2种气体的气缸:一个为氧气,一个为氮气。让潜水员下潜的深度需要各种的数量的氧和氮。潜水员有一定数量的气缸。每个气缸都有重量和气体容量。潜水员为了完成他的工作需要特定数量的氧和氮。他完成工作所需气缸的总重的最低限度的是多少?
例如:潜水员有5个气缸。每行三个数字为:氧,氮的(升)量和气缸的重量:
3 36 120
10 25 129
5 50 250
1 45 130
4 20 119
如果潜水员需要5升的氧和60升的氮则总重最小为249(1,2或者4,5号气缸)。
你的任务就是计算潜水员为了完成他的工作需要的气缸的重量的最低值。
【输入】
第一行有2整数m,n(1≤m≤21,1≤n≤79)。它们表示氧,氮各自需要的量。
第二行为整数k(1≤n≤1000)表示气缸的个数。
此后的k行,每行包括ai,bi,ci(1≤ai≤21,1≤bi≤79,1≤ci≤800)3ai,bi,ci(1≤ai≤21,1≤bi≤79,1≤ci≤800)3整数。这些各自是:第i个气缸里的氧和氮的容量及汽缸重量。
【输出】
仅一行包含一个整数,为潜水员完成工作所需的气缸的重量总和的最低值。
【输入样例】
5 60
5
3 36 120
10 25 129
5 50 250
1 45 130
4 20 119
【输出样例】
249
其实就是相当于费用加了一维,只需状态也加一维即可,则设f[i][v][u]为前i件物品付出两种代价v和u时可获得的最大价值。
状态转移方程:f[i][v][u]=max(f[i][v][u],f[i-1][v-a[i]][u-b[i]]+c[i]),如01背包转化为一维问题一样,
转化为二维f[v][u]=max(f[v][u],f[v-a[i]][u-b[i]]+c[i]),
这题和01背包的区别是背吧问题是用背包空间,这个是得到最小重量
#include <bits/stdc++.h>
using namespace std;
int main()
{
int n,m,i,k,j,f[102][102];
int a[6002],b[6002],c[6002];
cin>>m>>n>>k;
int t;
memset(f,127,sizeof(f));//把f初始化为一个很大的整数,我也纳闷127为啥,很大的整数
for(i=1;i<=k;i++)
cin>>a[i]>>b[i]>>c[i];
f[0][0]=0;
for(i=1;i<=k;i++)
for(j=m;j>=0;j--)
for(t=n;t>=0;t--)
{
int d1=j+a[i];//和01背包不同,它的a[i]和b[i]是可以超过的
int d2=t+b[i];
if(d1>m) d1=m;//若氮 和氧含量超过需求,直接等于需求
if(d2>n) d2=n;
f[d1][d2]=min(f[d1][d2],f[j][t]+c[i]);
}
cout<<f[m][n];
return 0;
}
六:分组的背包问题
有N件物品和一个容量为v的背包。第i件物品的费用是w[i],价值是c[i]。这些物品被划分为若干组,每组中的物品互相冲突,最多选一件。求解将哪些物品装入背包可使这些物品费用总和不超过背包容量,且价值总和最大
这个问题变成了每组物品有若干种策略:选择本组的某一件,还是一件都 不选。也就是说设f[k][v]表示前k组物品花费费用v能取得的最大权值,则有f[k][v]=max(f[k-1][v],f[k-1][v-w[i]]+c[i])物品i属于第k组
1272:【例9.16】分组背包
【题目描述】
一个旅行者有一个最多能装V公斤的背包,现在有n件物品,它们的重量分别是W1,W2,...,WnW1,W2,...,Wn,它们的价值分别为C1,C2,...,CnC1,C2,...,Cn。这些物品被划分为若干组,每组中的物品互相冲突,最多选一件。求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大。
【输入】
第一行:三个整数,V(背包容量,V≤200),N(物品数量,N≤30)和T(最大组号,T≤10);
第2..N+1行:每行三个整数Wi,Ci,PWi,Ci,P,表示每个物品的重量,价值,所属组号。
【输出】
仅一行,一个数,表示最大总价值。
【输入样例】
10 6 3 2 1 1 3 3 1 4 8 2 6 9 2 2 8 3 3 9 3
【输出样例】
20
#include<bits/stdc++.h>
using namespace std;
int w[31],c[31];
int a[11][31],f[201];
int v,n,t;
int main()
{
scanf("%d%d%d",&v,&n,&t);
for(int i=1;i<=n;i++){
int p;
scanf("%d%d%d",&w[i],&c[i],&p);
a[p][++a[p][0]]=i;//a[p][0]存储p组元素个数,a[p][x] 是存储元素序号
}
for(int k=1;k<=t;k++){
for(int j=v;j>=0;j--){
for(int i=1;i<=a[k][0];i++){
if(j>=w[a[k][i]]){
int temp=a[k][i];
f[j]=max(f[j],f[j-w[temp]]+c[temp]);
}
}
}
}
printf("%d\n",f[v]);
return 0;
}
七:背包问题的方案总数
对于一个给定了背包容量,物品费用,物品间相互关系(分组,依赖等)的背包问题,除了在给定每个物品的价值后求可得到的最大价值外,还可以得到装满背包或将背包装某一指定容量的方案总数
1273:【例9.17】货币系统
【题目描述】
给你一个n种面值的货币系统,求组成面值为m的货币有多少种方案。
【输入】
第一行为n和m。
【输出】
一行,方案数。
【输入样例】
3 10 //3种面值组成面值为10的方案 1 //面值1 2 //面值2 5 //面值5
【输出样例】
10 //有10种方案
#include<bits/stdc++.h>
using namespace std;
int n,m;
int a[10100];
long long int f[1000001];
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
}
f[0]=1;
for(int i=1;i<=n;i++){
for(int j=a[i];j<=m;j++){
f[j]+=f[j-a[i]];
}
}
printf("%lld\n",f[m]);
return 0;
}