1、01背包问题
朴素做法
#include<iostream>
using namespace std;
int n,V;
const int N = 1010;
int f[N][N],v[N],w[N];
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 = 1;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;
}
优化后 二维优化为一维重要的是做好方程的等价变换
#include<iostream>
using namespace std;
int n,V;
const int N = 1010;
int f[N],v[N],w[N];
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 = V;j >= v[i];j--)
f[j] = max(f[j],f[j - v[i]] + w[i]);
cout << f[V];
return 0;
}
要注意f[][]的定义,DP问题都是根据定义出发写出相应的状态转移方程。
2、完全背包问题
朴素做法
#include<iostream>
using namespace std;
const int N = 1010;
int f[N][N],v[N],w[N];
int m,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 = 1; j <= m;j++)
{
for(int k = 0;k * v[i] <= j;k++)
f[i][j] = max(f[i][j],f[i - 1][j - k * v[i]] + w[i] * k);
}
cout << f[n][m];
return 0;
}
优化后 方程变量代换优化
#include<iostream>
using namespace std;
const int N = 1010;
int f[N][N],v[N],w[N];
int m,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 = 1; j <= m;j++)
{
f[i][j] = f[i - 1][j];
if(j >= v[i]) f[i][j] = max(f[i][j],f[i][j - v[i]] + w[i]);
}
cout << f[n][m];
return 0;
}
再次优化后 二维变一维
#include<iostream>
using namespace std;
const int N = 1010;
int f[N],v[N],w[N];
int m,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++)
f[j] = max(f[j],f[j - v[i]] + w[i]);
cout << f[m];
return 0;
}
3、多重背包问题
本质上为完全背包问题
朴素做法
#include<iostream>
using namespace std;
const int N = 110;
int m,n,v[N],w[N],s[N],f[N][N];
int main(){
int m,n;
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 = 1; j<= m;j++)
for(int k = 0;k * v[i] <= j && k <= s[i];k++)
f[i][j] = max(f[i][j],f[i - 1][j - k * v[i]] + k * w[i]);
cout << f[n][m];
return 0;
}
4、分组背包问题
#include<iostream>
using namespace std;
const int N = 110;
int m,n,v[N][N],w[N][N],f[N][N],s1[N];
int main(){
cin >> n >> m;
for(int i = 1;i <= n;i++)
{
int s;
cin >> s;
s1[i] = s;
for(int j = 1;j <= s; j++){
cin >> v[i][j] >> w[i][j];
}
}
//f[i][j] = max(f[i- 1][j],f[i - 1][j - v[i]])
for(int i = 1;i <= n;i++)
for(int j = 1;j <= m;j++){
f[i][j] = f[i - 1][j]; //这个要放在第二个循环里,不能放在第三个循环。易错!
for(int k = 1;k <= s1[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];
return 0;
}
注意易错点。
优化后 二维变一维 注意j从大到小遍历。保证f[j - v[i][k]] 的含义为上一层的值,即f[i - 1][j - v[i][k]]
#include<iostream>
using namespace std;
const int N = 110;
int m,n,v[N][N],w[N][N],f[N],s1[N];
int main(){
cin >> n >> m;
for(int i = 1;i <= n;i++)
{
int s;
cin >> s;
s1[i] = s;
for(int j = 1;j <= s; 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 <= s1[i];k++)
if(j >= v[i][k]) f[j] = max(f[j],f[j - v[i][k]] + w[i][k]);
}
cout << f[m];
return 0;
}
5、总结
背包问题f[i][j]的含义为前i个物品中体积为j时的最大价值。
状态转移方程 划分状态时主要思路是分为是否选第i个物品分情况讨论。找出最大值。