参考课程是我高中信息竞赛邱老师的课程。
【16-3-1 背包问题】 https://www.bilibili.com/video/BV1LZ421475D/?share_source=copy_web&vd_source=2c56c6a2645587b49d62e5b12b253dca
【16-3-2 01背包】 https://www.bilibili.com/video/BV1XH4y1M7iZ/?share_source=copy_web&vd_source=2c56c6a2645587b49d62e5b12b253dca
【16-3-3 完全背包】 https://www.bilibili.com/video/BV1Rs421A7y2/?share_source=copy_web&vd_source=2c56c6a2645587b49d62e5b12b253dca
【16-3-4 多重背包】 https://www.bilibili.com/video/BV1RD421A7Yh/?share_source=copy_web&vd_source=2c56c6a2645587b49d62e5b12b253dca
【16-3-5 混合背包】 https://www.bilibili.com/video/BV16y411Y7ua/?share_source=copy_web&vd_source=2c56c6a2645587b49d62e5b12b253dca
【16-3 背包习题解答】 https://www.bilibili.com/video/BV1UM4m1k7EM/?share_source=copy_web&vd_source=2c56c6a2645587b49d62e5b12b253dca
完整CSP ACM板子可以在我资料获取,我设置的0积分,如果要花钱来B站私我!
背包问题

01背包
如果有多个状态,用struct Node存储,不要用现成的数据结构。

空间优化——逆向滚动数组
从后向前覆盖
for(int i=1;i<=r;i++){
for(int j=i;j>=1;j--){
int t;
cin>>t;
F[j]=max(F[j],F[j-1])+t;
}
}
for(int i=1;i<=M;i++){//对于每一棵草都判断(对每一件物品判断)
for(int t=T;t>=0;t--){(如果装得下(倒着装))
if(t>=herb[i].t){
F[t]=max(F[t],F[t-herb[i].t]+herb[i].v);(取或不取)
}
}
}
P1048 采药问题
https://www.luogu.com.cn/problem/P1048

#include<bits/stdc++.h>
using namespace std;
//时间、价值、没有数量限制,最多100
//时间是容量 价值是价值 如果如果100助都能采集完就输出100,如果不能就是01背包
struct Node{
int t,v;
};
int T,M;
Node herb[105];
int F[1005];
int main(){
cin>>T>>M;
for(int i=1;i<=M;i++){
cin>>herb[i].t>>herb[i].v;
}
F[0]=0;
for(int i=1;i<=M;i++){//对于每一棵草都判断
for(int t=T;t>=0;t--){
if(t>=herb[i].t){
F[t]=max(F[t],F[t-herb[i].t]+herb[i].v);
}
}
}
cout<<F[T];
}
常数优化
实际上如果背包很大,那么后面如果时间足够就不需要算了,比如还剩250秒,还剩20颗,这20颗的花费时间是200秒,那之间用F[40]计算输出即可。在循环中当时间t大于采集完后面所有草药和的时候,就直接全选跳出。
用一个逆序后缀和存储.

练习 P2925
https://www.luogu.com.cn/problem/P2925

#include<bits/stdc++.h>
using namespace std;
int C,H;
int hay[50005];
int F[50005];
//F[j]存容积为i时装的最大体积
//hay[i]存每个稻草的体积
int main(){
cin>>C>>H;
for(int i=1;i<=H;i++){
cin>>hay[i];
}
for(int i=1;i<=H;i++){
for(int j=C;j>=hay[i];j--){
F[j]=max(F[j],F[j-hay[i]]+hay[i]);//不选则不变 选则容积减少hay[i]
}
}
cout<<F[C];
}
完全背包

转移方程:
取这个类——取一件,取完还能取这个类! F[i][v-vi]+wi 下次还能取i
不取这个类——F[i-1][v]
空间优化——正向滚动数组
for(int i=1;i<=m;i++){
for(int j=herb[i].t;j<=T;j++){
F[j]=max(F[j],F[j-herb[i].t]+herb[i].v);
}
}

练习 P1616
https://www.luogu.com.cn/problem/P1616

#include<bits/stdc++.h>
using namespace std;
struct Node{
int t,v;
};
int presum[10005];
Node herb[10005];//草药的种类10005 herb存草药的时间和价值
int T,m;
long long F[100000005];//记录F[i]时间下最大价值
int main(){
cin>>T>>m;
for(int i=1;i<=m;i++){
cin>>herb[i].t>>herb[i].v;
}
for(int i=1;i<=m;i++){
for(int j=herb[i].t;j<=T;j++){
F[j]=max(F[j],F[j-herb[i].t]+herb[i].v);
}
}
cout<<F[T];
}
练习 P2722
https://www.luogu.com.cn/problem/P2722

#include<bits/stdc++.h>
using namespace std;
struct Node{
int t,s;
};
Node Q[10005];
int m,n;
int F[100005];
int main(){
cin>>m>>n;
for(int i=1;i<=n;i++){
cin>>Q[i].s>>Q[i].t;
}
for(int i=1;i<=n;i++){
for(int j=Q[i].t;j<=m;j++){
F[j]=max(F[j],F[j-Q[i].t]+Q[i].s);
}
}
cout<<F[m];
}
多重背包

k就是放多少件,不放就是0,最多放p[i]。
对于当前物品,只有取k件然后进入下一个物品的选择。
练习 P1776
https://www.luogu.com.cn/problem/P1776

空间优化——逆向滚动数组

#include<bits/stdc++.h>
using namespace std;
int n,W;
struct Node{
int v,w,m;
};
Node P[105];
int F[100005];//存当前空间下的最大价值
int main(){
cin>>n>>W;
for(int i=1;i<=n;i++){
cin>>P[i].v>>P[i].w>>P[i].m;
}
for(int i=1;i<=n;i++){//对于每类宝物
for(int j=W;j>=0;j--){
for(int k=0;k<=P[i].m&&j>=k*P[i].w;k++){
F[j]=max(F[j],F[j-k*P[i].w]+k*P[i].v);
}
}
}
cout<<F[W];
}
时间优化——二进制拆分
要x件可以拆为一堆二进制堆,看看要不要这一堆,就变成了logn的01背包问题

注意定义,下面的模板 w是价值 v是空间。和我习惯相反。下下面是我的模板。

#include<bits/stdc++.h>
using namespace std;
int n,W;
int cnt=1;
int w[105],v[105],m[105];
int w1[100005],v1[100005];
int F[100005];//存当前空间下的最大价值
int main(){
cin>>n>>W;
for(int i=1;i<=n;i++){
cin>>v[i]>>w[i]>>m[i];
int temp=1;
while(temp<=m[i]){//二进制拆分 当temp<=m[i] 即二进制数<个数时
w1[cnt]=temp*w[i];//把temp个物品的重量合并,塞进w1中
v1[cnt]=temp*v[i];//把temp个物品的价值合并,塞进v1中
cnt++;
m[i]-=temp;//从i类物品数量中减去拆走的部分
temp<<=1;
}
w1[cnt]=m[i]*w[i];//剩余的重量放一堆
v1[cnt]=m[i]*v[i];//剩余的价值放一堆
cnt++;//下一类物品继续拆
}
//一定要注意,后面转换为01背包后 用的变量都是w1 v1修改后的内容了。
for(int i=1;i<=cnt;i++){
for(int j=W;j>=w1[i];j--){
F[j]=max(F[j],F[j-w1[i]]+v1[i]);
}
}
cout<<F[W];
}
混合背包
三种背包的初始状态 状态定义 最终目的都是一样的,唯一不同的是转移方程。
因此可以提前判断,然后根据不同的条件写for,从前往后从后往前滚动数组。

另一种想法是,混合背包就是多重背包,对于01背包本身就是多重的一种,完全背包也不是无限的,而是有界的,最多装:背包大小/物品大小。
处理链:【混合->多重->二进制拆分->01背包】

模板 P1833 樱花
处理链:【混合->多重->二进制拆分->01背包】
https://www.luogu.com.cn/problem/P1833

使用 cin 配合字符变量
用 cin,可以定义一个 char 变量专门用来“吃掉”冒号。
int h1, m1, h2, m2, n;
char c; // 用来接收冒号
cin >> h1 >> c >> m1 >> h2 >> c >> m2 >> n;
#include<bits/stdc++.h>
using namespace std;
int w[100005],v[100005],P[100005];
int w1[100005],v1[100005];
//n是数量 T是空间
int F[100005];
int cnt=1;
int main(){
int h1,h2,m1,m2,n,T;
char c;
cin>>h1>>c>>m1>>h2>>c>>m2>>n;
T=abs(h1*60+m1-h2*60-m2);
for(int i=1;i<=n;i++){
int p;
cin>>w[i]>>v[i]>>p;
if(p==0){
P[i]=T/w[i];//最大容量/当前物品体积=最大数量
}else{
P[i]=p;
}
int temp=1;
while(temp<=P[i]){
w1[cnt]=temp*w[i];
v1[cnt]=temp*v[i];
cnt++;
P[i]-=temp;
temp<<=1;
}
w1[cnt]=P[i]*w[i];
v1[cnt]=P[i]*v[i];
cnt++;
}
for(int i=1;i<=cnt;i++){
for(int j=T;j>=w1[i];j--){
F[j]=max(F[j],F[j-w1[i]]+v1[i]);
}
}
cout<<F[T];
}


——动态规划-背包问题&spm=1001.2101.3001.5002&articleId=162050576&d=1&t=3&u=d72ced4f2b9c4bc097047704565d7365)
1512

被折叠的 条评论
为什么被折叠?



