目录
一、算法介绍
是把大问题拆分成小问题,通过寻找大问题与小问题的递推关系,解决一个个小问题,最终达到解决原问题的效果,具有记忆性,通过填写表把所有已经解决的子问题答案纪录下来,在新问题里需要用到的子问题可以直接提取,避免了重复计算,从而节约了时间,所以在问题满足最优性原理之后,用动态规划解决问题的核心就在于填表,表填写完毕,最优解也就找到。
关于动态规划的时间复杂度计算: 状态表示的量*转移方程
二、01背包
含义:有n件物品和容量为v的背包,每件物品体积是vi,权值是wi。
特点:每件物品只有一个(最多用一次)。
有 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
解析:
二维:
#include<iostram>
#include<algorithm>
using namespace std;
const int N=1010;
int n,m; //n件物品,背包容量为m
int v[N],W[N]; //分别表示物品体积和价值
int f[N][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=0;j<=m;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][m]<<endl;
return 0;
}
三、完全背包
特点:每件物品有无限个
有 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
输出样例:
10
解析:
#include<iostream>
#include<algorithm>
using namespace std;
const int N = 1010;
int n,m;//表示物品种数和背包容积
int v[N],w[N];
int f[N][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=0;j<=m;j++){
for(int k=0;k*v[i]<=j;k++){
f[i][j]=max(f[i][j], f[i-1][j-v[i]*k]+w[i]*k);
}
}
}
cout<<f[n][m]<<endl;
return 0;
}
四、多重背包
特点:每个物品有si个(不同物品可能不一样)。
有 N 种物品和一个容量是 V 的背包。
第 i 种物品最多有 si 件,每件体积是 vi,价值是 wi。
求解将哪些物品装入背包,可使物品体积总和不超过背包容量,且价值总和最大。
输出最大价值。
输入格式
第一行两个整数,N,V,用空格隔开,分别表示物品种数和背包容积。
接下来有 N 行,每行三个整数 vi,wi,si,用空格隔开,分别表示第 i 种物品的体积、价值和数量。
输出格式
输出一个整数,表示最大价值。
数据范围
0<N,V≤100
0<vi,wi,si≤100
输入样例
4 5
1 2 3
2 4 1
3 4 3
4 5 2
输出样例:
10
解析:
#include<iostream>
#include<algorithm>
using namespace std;
const int N = 110;
int n,m; //n件物品,背包容积为m
int v[N],w[N],s[N];
int f[N][N];
int main()
{
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=0;j<=m;j++){
for(int k=0; k<=s[i]&&k*v[i]<=j;j++){
f[i][j]=max(f[i][j], f[i-1][j-v[i]*k] + w[i]*k);
}
}
}
cout<<f[n][m]<<endl;
return 0;
}
扩展:
当时间复杂度过高时,在多重背包这里常见的是二进制优化(即先将数据打包,再使用01背包)
例:即将数据范围改成如下:
0<N≤1000
0<V≤2000
0<vi,wi,si≤2000
#include<iostream>
#include<algorithm>
using namespace std;
const int N=25000,M=2010;
int n,m; //n件物品,背包容积为m
int v[N],w[N];
int f[N];
int main()
{
cin>>n>>m;
int cnt = 0;
for(int i=1;i<=n;i++)
{
int a,b,s; //分别表示第i种物品的体积、价值和数量
cin>>a>>b>>s;
int k=1;
while(k<=s)
{
cnt++;
v[cnt] = a*k;
w[cnt] = b*k;
s-=k;
k*=2;
}
if(s>0)
{
cnt++;
v[cnt]=a*s;
w[cnt]=b*s;
}
}
n=cnt;
for(int i=1;i<=n;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;
}
五、分组背包
特点:一共有N组,每一组里有若干个物品,每一组里最多只能选一个物品。
有 N 组物品和一个容量是 V 的背包。
每组物品有若干个,同一组内的物品最多只能选一个。
每件物品的体积是 vij,价值是 wij,其中 i 是组号,j 是组内编号。
求解将哪些物品装入背包,可使物品总体积不超过背包容量,且总价值最大。
输出最大价值。
输入格式
第一行有两个整数 N,V,,用空格隔开,分别表示物品组数和背包容量。
接下来有 N 组数据:
- 每组数据第一行有一个整数 Si,表示第 i 个物品组的物品数量;
- 每组数据接下来有 Si 行,每行有两个整数 vij,wij,用空格隔开,分别表示第 i 个物品组的第 j 个物品的体积和价值;
输出格式
输出一个整数,表示最大价值。
数据范围
0<N,V≤100
0<Si≤100
0<vij, wij≤100
输入样例
3 5
2
1 2
2 4
1
3 4
1
4 5
输出样例:
8
解析:
#include<iostream>
#include<algorithm>
using namespace std;
const int N=110;
int n,m;//n件物品,背包容积为m
int v[N][N],w[N][N],s[N];//s表示第i个物品组的物品数量
int f[N][N]; //只从前i组物品中选,当前体积小于j的最大值
int main()
{
cin>>n>>m;
for(int i=1;i<=n;i++)
{
cin>>s[i];
for(int j=0;j<s[i];j++){
cin>>v[i][j]>>w[i][j];
}
}
for(int i=1;i<=n;i++){ //第i个物品组
for(int j=0;j<=m;j++){ //容积
f[i][j]=f[i-1][j]; //如果不选
for(int k=1;k<=s[i];k++){ //物品组中选几个
if(j>=v[i][k] f[i][j]=max(f[i][j],f[i-1][j-v[i][k]]+w[i][k]);
}
}
}
cout<<f[n][m]<<endl;
return 0;
}