目录
01背包问题
最基本的
#include<iostream>
#include<algorithm>
using namespace std;
const int N = 1010;
int f[N][N]; // 背包容积为j时,从前i件物品中可以取得的最大值
int v[N], w[N]; // v价值, w重量
int main()
{
int n,m;
cin >> n >> m;
for ( int i = 1; i <= n; i++ ) cin >> w[i] >> v[i];
for ( int i = 1; i <= n; i++ )
for ( int j = 1; j <= m; j++ )
f[i][j] = max( f[i-1][j], f[i-1][j-w[i]]+v[i] ); //状态转移
cout << f[m] << endl;
// f初始全为0,f[m]就是答案, 若只有f[0][0]初始为0,需要遍历最后一行找最大值
// 后一种找到的最大值的位置即前一种最大值第一次出现位置,该位置之后均为-INF
return 0;
}
优化:一维数组
#include<iostream>
#include<algorithm>
using namespace std;
const int N = 1010;
int f[N];
int v[N], w[N];
int main()
{
int n,m;
cin >> n >> m;
for ( int i = 1; i <= n; i++ ) { //大循环每层多放出件物品,其实i是不是从0->n无所谓的,只要遍历了n件物品即可
cin >> w >> v; //这里也不需要再开数组了
for ( int j = m; j >= w[i]; j-- ) { //j从后向前遍历是为了不影响同一层(当前处理同一件物品)中靠前的j对应的f值
//采用一维数组, 每一轮循环中f[j]相当于继承了上一轮的值,只要判断把当前这件放入j空间的背包能不能取得更高的效益即可
f[j] = max( f[j], f[j-w]+v );
}
}
cout << f[m] << endl; //价值提高的可能总是先出现在数组右边,且总有f[i]>=f[i-1]
return 0;
}
完全背包
#include <iostream>
using namespace std;
const int N = 1010;
int f[N];
int main()
{
int n,m,v,w;
cin >> n >> m;
for ( int i = 1; i <= n; i++ ) {
cin >> w >> v;
for ( int j = w; j <= m; j++ ) { //从小到大循环,后面添加的物品叠加在前面的里
f[j] = max( f[j], f[j-w]+v );
}
}
cout << f[m] << endl;
return 0;
}
多重背包
三循环
#include <iostream>
#include <climits>
using namespace std;
const int N = 1010;
int f[N];
int main()
{
int n,m;
int v,w,s;
cin >> n >> m;
for ( int i = 1; i <= n; i++ ) {
cin >> w >> v >> s;
for ( int j = m; j >= w; j-- ) {
for ( int k = 1; k <= s && k*w<=j ; k++ ) {
f[j] = max( f[j], f[j-k*w]+k*v );
}
}
}
cout << f[m] << endl;
}
二进制优化
把每组物品按规则拆开加入物品栏中,然后按01背包做
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
struct node{
int v,w;
};
int main()
{
int n, m;
cin >> n >> m;
vector<node> vec;
int v,s,w;
for ( int i = 0; i < n; i ++ ) {
cin >> w >> v>> s;
for ( int j = 1; j <= s; j *= 2 ) {
s -= j;
vec.push_back( {v*j, w*j} );
}
if( s ) vec.push_back( {v*s, w*s} );
}
int f[m+5] = {0};
for ( int i = 0; i < vec.size(); i ++ ) {
for ( int j = m; j >= vec[i].w; j-- ) {
f[j] = max( f[j], f[j-vec[i].w]+vec[i].v );
}
}
cout << f[m] << endl;
}
单调队列优化
对每一个物品i,将状态 f[i][j] 按(j%w)划分成w组,对每一组按单调队列做
#include <iostream>
using namespace std;
const int N = 20010;
int dp[N], pre[N], q[N]; // q单调队列
int n, m;
int main() {
cin >> n >> m;
for (int i = 0; i < n; ++i) { // 对第i件物品
memcpy(pre, dp, sizeof(dp)); // pre是dp的一个副本
int v, w, s;
cin >> v >> w >> s;
for (int j = 0; j < v; ++j) { // j为该物品的分组
int head = 0, tail = -1; // 队列头尾 , 闭区间
for (int k = j; k <= m; k += v) { // 每组中从小到大的容积
if (head <= tail && k - s*v > q[head]) // head <= tail 即队列不空
++head; // k代表空间,如果把这件物品全放进去了还比队列头大(k>=j+s*v),就把头出队,因为头之后的元素更大且符合限制条件
while (head <= tail && pre[q[tail]] - (q[tail] - j)/v * w <= pre[k] - (k - j)/v * w)
--tail;
if (head <= tail)
dp[k] = max(dp[k], pre[q[head]] + (k - q[head])/v * w);
q[++tail] = k;
}
}
}
cout << dp[m] << endl;
return 0;
}