以下模板题目都可在acwing找到
一、01背包问题
之前有博客写过01背包和完全背包的详解:
背包问题
需要注意的就是01背包用的是上一层的 f 数组,所以对体积的循环是从大到小,而完全背包是从小到大。
代码:
#include<bits/stdc++.h>
using namespace std;
const int N = 1010;
int n,m;
int f[N],v[N],w[N];
//int f[N][N],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 = 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];*/
//一维优化
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];
return 0;
}
二、完全背包问题
代码:
#include<bits/stdc++.h>
using namespace std;
const int N = 100010;
int f[N],v[N],w[N],n,m;
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;
}
三、多重背包问题Ⅰ
与01背包不同的地方在于它的每个物品都有一定的数量不止一个。
由于数据范围都小于100.
最朴素的做法对每个物品的每一个都进行一次枚举,复杂度n * m * k.
代码:
#include<bits/stdc++.h>
using namespace std;
const int N = 110;
int f[N],v[N],w[N],s[N],n,m;
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 = m; j >= v[i]; j --)//由于是01背包的模板所以从大到小
for(int k = 1; k <= s[i] && k * v[i] <= j; k ++ )//每个物品的每一个都枚举
f[j] = max(f[j],f[j - v[i] * k] + w[i] * k);
cout<<f[m];
return 0;
}
四、多重背包问题Ⅱ
数据范围来到了1000,用朴素做法会超时,所以对朴素做法进行一个二进制优化,对每一种物品的 s 个数量,分别分成1 2 4 8 …这样的物品再分别使用01背包模板。分完堆总共有n * lgk 堆,复杂度 n * lgk * m.
代码:
#include<bits/stdc++.h>
using namespace std;
const int N = 2010;
int f[N],w[N],v[N],s[N],n,m;
struct goods
{
int v,w;
};
int main()
{
vector<goods>good;
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 k = 1; k <= s[i]; k *= 2)
{
s[i] -= k;
good.push_back({k * v[i],k * w[i]});//分堆并加入数组
}
if(s[i])//如果不是二的整数次幂会多出一堆
good.push_back({s[i] * v[i],s[i] * w[i]});
}
for(auto i : good)//对每一堆进行01背包模板
for(int j = m; j >= i.v; j -- )
f[j] = max(f[j],f[j - i.v] + i.w);
cout<<f[m];
return 0;
}
五、多重背包问题Ⅲ
数据范围再加大,普通的二进制优化已经不能满足题目的要求了。
这里采用的是单调队列优化,代码已经写了注释,自己理解的不是很清楚具体思路可以看多重背包问题 III 详解 + yxc大佬代码解读
代码:
#include<bits/stdc++.h>
using namespace std;
const int N = 20010;
int pre[N],dp[N],q[N];//pre表示 i- 1 层的最大价值 dp表示 i 层的最大价值 q队列中存的是体积
//是一个单调递减队列,队头表示最大价值的体积
int n,m;
int main()
{
cin>>n>>m;
for(int i = 1; i <= n; i ++ )//枚举每一层
{
memcpy(pre,dp,sizeof(dp));//复制上一层结果
int v,w,s;
cin>>v>>w>>s;
for(int j = 0; j < v; j ++ )//分别 j 个单调队列进行操作 (j + k * v)
{
int head = 0,tail = -1;
for(int k = j; k <= m; k += v)
{
while(head <= tail && k - q[head] > s * v)//枚举到的体积和队头体积之差除以 v 大于 s 表示超过了数量
head ++;//队头出队
//单调递减队列,所以队尾比要入队的元素价值小的话2出队
while(head <= tail && pre[q[tail]] - q[tail]/ v * w <= pre[k] - k / v * w)
tail -- ;
if(head <= tail)
dp[k] = pre[q[head]] + (k - q[head]) / v * w;// 更新最大值(没有很明白)
q[ ++ tail] = k;//进队
}
}
}
cout<<dp[m];
return 0;
}
六、混合背包问题
将01背包、完全背包、多重背包放一起的缝合怪,数据1000,多重背包用二进制优化可以过。将多重背包用二进制优化分成堆就和01背包一样操作,对于完全背包将体积循环反过来即可。
代码:
#include<bits/stdc++.h>
using namespace std;
const int N = 1010;
int n,m,f[N];
struct good//结构体存
{
int kind;
int v,w;
};
int main()
{
vector<good>goods;
cin>>n>>m;
for(int i = 1; i <= n; i ++ )
{
int v,w,s;
cin>>v>>w>>s;
if(s < 0)//01背包
goods.push_back({-1,v,w});
else if(!s)//完全背包
goods.push_back({0,v,w});
else//分堆再01背包
{
for(int k = 1; k <= s; k *= 2)
{
s -= k;
goods.push_back({-1,v * k,w * k});
}
if(s)
goods.push_back({-1,v * s,w * s});
}
}
for(auto i : goods)
{
if(i.kind == -1)
for(int j = m; j >= i.v; j -- )
f[j] = max(f[j],f[j - i.v] + i.w);
else
for(int j = i.v; j <= m; j ++ )
f[j] = max(f[j],f[j - i.v] + i.w);
}
cout<<f[m];
return 0;
}
七、二维费用的背包问题
除体积限制之外有多了一个重量限制,多加一个循环即可。
代码:
#include<bits/stdc++.h>
using namespace std;
const int N = 1100;
int n,M,l,v[N],m[N],w[N],f[110][110];
int main()
{
cin>>n>>M>>l;
for(int i = 1; i <= n ; i ++ )
cin>>v[i]>>m[i]>>w[i];
for(int i = 1; i <= n; i ++ )
for(int j = M; j >= v[i]; j -- )
for(int k = l; k >= m[i]; k -- )
f[j][k] = max(f[j][k],f[j - v[i]][k - m[i]] + w[i]);
cout<<f[M][l];
return 0;
}
八、分组背包问题
多加一个从每组里选一个的循环即可
代码:
#include<bits/stdc++.h>
using namespace std;
const int N = 110;
int v[N],w[N],f[N],n,m;
int main()
{
int n,m;
cin>>n>>m;
for(int i = 1; i <= n; i ++ )
{
int x;
cin>>x;
for(int j = 1; j <= x; j ++ )
cin>>v[j]>>w[j];
for(int j = m; j >= 0; j --)
for(int k = 1; k <= x; k ++ )//每组选一个
if(j >= v[k])
f[j] = max(f[j],f[j - v[k]] + w[k]);
}
cout<<f[m];
return 0;
}