1. 经典模型
1.1 01背包
dp[i][j] 表示前i件物品 装 体积不超过j的 最大价值
dp[i][j] = max(dp[i-1][j] , dp[i-1][j - v[i]] + w[i])
1.1.1 二维空间
#include<iostream>
using namespace std;
const int N = 1010;
int dp[N][N];
int n ,m;
int v[N],w[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 ++) // 0件物品 最大值 必为0
for(int j = 0 ; j <= m ; j ++) // dp[i][j] 表示从前i件物品中装入体积不超过j的最大价值
if(j >= v[i])
dp[i][j] = max(dp[i-1][j] , dp[i-1][j - v[i]] + w[i]);
else dp[i][j] = dp[i-1][j];
cout << dp[n][m] << endl;
}
1.1.2 一维空间(空间优化)
- 滚动数组
- 01背包优化后,体积倒序
#include<iostream>
using namespace std;
const int N = 1010;
int dp[N];
int n ,m;
int v[N],w[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 --)
dp[j] = max(dp[j] , dp[j - v[i]] + w[i]);
cout << dp[m] << endl;
}
1.2 完全背包
1.2.1 枚举 + dp
#include<iostream>
const int N = 1010;
using namespace std;
int n , m;
int v[N] , w[N];
int dp[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 ++)
// 注意k可取0,便可得到 从i-1件物品中选择体积不超过j的最大价值,故无需判断
dp[i][j] = max(dp[i][j] , dp[i-1][j - k * v[i] ] + k * w[i]);
cout << dp[n][m] << endl;
return 0;
}
1.2.2 二维空间优化
- v表示v[i],w表示w[i],即v为第 i 件物品的体积,w为第 i 件物品的价值
dp[ i , j ] = max { dp[ i - 1, j ] , dp[ i - 1 , j - v ] + w, dp[ i - 1, j - 2v ] + 2w , dp[ i - 1, j - 3 v] + 3w , … … }
dp[ i , j - v ] = max { dp[ i - 1, j - v ] , dp[ i - 1 , j - 2v ] + w , dp[ i - 1, j - 3v ] + 2w , … … }
2. 显然 dp[ i ,j ] = max { dp[ i - 1 , j ] , dp[ i , j - v ] }
#include<iostream>
const int N = 1010;
using namespace std;
int n , m;
int v[N] , w[N];
int dp[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 ++)
if(v[i] <= j)
dp[i][j] = max(dp[i-1][j] , dp[i][j - v[i] ] + w[i]);
else dp[i][j] = dp[i-1][j];
cout << dp[n][m] << endl;
return 0;
}
1.2.3 一维空间优化
- 完全背包,体积顺序
#include<iostream>
const int N = 1010;
using namespace std;
int n , m;
int v[N] , w[N];
int dp[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 = v[i] ; j <= m ; j ++)
dp[j] = max(dp[j] , dp[j - v[i] ] + w[i]);
cout << dp[m] << endl;
return 0;
}
01背包 | 完全背包 | |
---|---|---|
体积 j 的变化 | m -> v[ i ] | v[i] -> m |
状态转移 | dp[ j ] = max( dp[ j ] , dp[ j - v[ i ] ] + w[ i ] ); | dp[ j ] = max( dp[ j ] , dp[ j - v[ i ] ] + w[ i ] ); |
- 显然只有体积 j 的变化
1.3 多重背包
1.3.1 二维空间
- 与完全背包类似,只不过 k 限制条件 增加了 k <= s[ i ]
- 完全背包的k的限制条件只有 k * v[ i ] <= j
#include<iostream>
using namespace std;
const int N = 110;
int v[N] , w[N] , s[N];
int dp[N][N];
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 = 0 ; j <= m ; j ++)
for(int k = 0 ; k <= s[i] && k * v[i] <= j ; k ++)
dp[i][j] = max(dp[i][j] , dp[i - 1][j - k * v[i]] + k * w[i]);
cout << dp[n][m] << endl;
return 0;
}
1.3.2 二维空间 + 二进制优化
二进制优化
- 1024 可有 1 ,2 , 4 , 8 ,… … , 512 凑出来
- 故 第 i 中 物品数量 s[ i ] ,可由 1 , 2 , 3 ,… … , 2 ^ (k - 1) , s[ i ] - 2 ^ k 凑出;
2 ^ (k - 1) < s[ i ] < 2 ^ k - 最后展开为01背包问题
#include<iostream>
using namespace std;
const int N = 13010;
int dp[N][N];
int v[N], w[N];
int n , m;
int main(){
cin >> n >> m;
int cnt = 0;
for(int i = 1 ; i <= n ; i ++)
{
int a , b , s;
cin >> a >> b >> s;
int k = 1;
while( k <= s){
cnt ++;
v[cnt] = k * a;
w[cnt] = k * b;
s -= k;
k *= 2;
}
if( s > 0)
{
cnt ++;
v[cnt] = s * a;
w[cnt] = s * b;
}
}
n = cnt;
for(int i = 1 ; i <= n ; i ++)
for(int j = 0 ; j <= m ; j ++)
{
dp[i][j] = dp[i-1][j];
if(v[i] <= j)
dp[i][j] = max(dp[i][j] , dp[i-1][j-v[i]] + w[i]);
}
cout << dp[n][m] << endl;
}
1.3.3 一维空间
#include<iostream>
using namespace std;
const int N = 11010;
int dp[N];
int v[N], w[N];
int n , m;
int main(){
cin >> n >> m;
int cnt = 0;
for(int i = 1 ; i <= n ; i ++)
{
int a , b , s;
cin >> a >> b >> s;
int k = 1;
while( k <= s){
cnt ++;
v[cnt] = k * a;
w[cnt] = k * b;
s -= k;
k *= 2;
}
if( s > 0)
{
cnt ++;
v[cnt] = s * a;
w[cnt] = s * b;
}
}
n = cnt;
for(int i = 1 ; i <= n ; i ++)
for(int j = m ; j >= v[i] ; j --)
dp[j] = max(dp[j] , dp[j-v[i]] + w[i]);
cout << dp[m] << endl;
}
1.4 分组背包
- 01背包变形
- 每组只能取一件,每组遍历一遍即可
1.4.1 二维空间
#include<iostream>
using namespace std;
const int N = 110;
int v[N][N] , w[N][N] ,s[N];
int dp[N][N];
int n , m;
int main(){
cin >> n >> m;
for(int i = 1 ; i <= n ; i ++)
{
cin >> s[i];
for(int j = 1 ; j <= s[i] ; j ++)
cin >> v[i][j] >> w[i][j];
}
for(int i = 1 ; i <= n ; i ++)
for(int j = 0 ; j <= m ; j ++)
{
dp[i][j] = dp[i-1][j];
for(int k = 1 ; k <= s[i] ; k ++)
if(v[i][k] <= j)
dp[i][j] = max(dp[i][j] , dp[i-1][j - v[i][k]] + w[i][k]);
}
cout << dp[n][m] << endl;
return 0;
}
1.4.2 一维空间
#include<iostream>
using namespace std;
const int N = 110;
int v[N][N] , w[N][N] ,s[N];
int dp[N];
int n , m;
int main(){
cin >> n >> m;
for(int i = 1 ; i <= n ; i ++)
{
cin >> s[i];
for(int j = 1 ; j <= s[i] ; j ++)
cin >> v[i][j] >> w[i][j];
}
for(int i = 1 ; i <= n ; i ++)
for(int j = m ; j >= 0 ; j --)
for(int k = 1 ; k <= s[i] ; k ++)
if(v[i][k] <= j)
dp[j] = max(dp[j] , dp[j - v[i][k]] + w[i][k]);
cout << dp[m] << endl;
return 0;
}