【若归】背包dp做题笔记
前言:
现在决定未来,未来与过去无关。--波波
前置知识:
dd_engi的背包九讲(新版转载)
|
背包九讲——全篇详细理解与代码实现
背包问题 (附单调队列优化多重背包
|
背包问题入门(单调队列优化多重背包
题目来自学校OJ、洛谷、ybtOJ。

学校OJ:
A.采药
01背包(后面解题过程大部分均由01背包变形)板子题,加上一个滚动数组优化,没什么好说的。
$ 1<=i<=m,t>=j>=w[i]$
$f[j]=max(f[j],f[j-w[i]]+c[i]) $
#include<bits/stdc++.h>
using namespace std;
int w[10001],c[10001],f[10001];
int main()
{
int t,m,i,j;
cin>>t>>m;
for(i=1;i<=m;i++)
{
cin>>w[i]>>c[i];
}
for(i=1;i<=m;i++)
{
for(j=t;j>=w[i];j--)
{
f[j]=max(f[j],f[j-w[i]]+c[i]);
}
}
cout<<f[t]<<endl;
return 0;
}
B.开心的金明
与A类似,还是01背包板子,设第\(j\)件物品的价格为\(v[j]\),重要度为\(w[j]\),则第\(j\)件物品的价值为\(v[j]*w[j](j<=n)\)。
$ 1<=i<=m,n>=j>=v[i]$
$f[j]=max(f[j],f[j-v[i]]+p[i]) $
#include<bits/stdc++.h>
using namespace std;
int v[750001],p[750001],f[750001];
int main()
{
int n,m,i,j;
cin>>n>>m;
for(i=1;i<=m;i++)
{
cin>>v[i]>>p[i];
p[i]*=v[i];
}
for(i=1;i<=m;i++)
{
for(j=n;j>=v[i];j--)
{
f[j]=max(f[j],f[j-v[i]]+p[i]);
}
}
cout<<f[n]<<endl;
return 0;
}
C. 完全背包问题
完全背包板子(优化后面讲),因范围较小,选用不加优化的做法。
完全背包板子和和01背包板子仅有j的循环次序不同(注意一下,不要混淆),同时注意一下输出格式。
$ 1<=i<=n,w[i]<=j<=m$
$f[j]=max(f[j-w[i]]+c[i],f[j]) $
#include<bits/stdc++.h>
using namespace std;
int n,m,i,j,c[31],w[31],f[2001];
int main()
{
cin>>m>>n;
for(i=1;i<=n;i++)
{
cin>>w[i]>>c[i];
}
for(i=1;i<=n;i++)
{
for(j=w[i];j<=m;j++)
{
f[j]=max(f[j-w[i]]+c[i],f[j]);
}
}
cout<<"max="<<f[m];
return 0;
}
D.竞赛总分
P2722 [USACO3.1]总分 Score Inflation
完全背包板子(同C),加强一下印象。
$ 1<=i<=n,w[i]<=j<=m$
$f[j]=max(f[j-w[i]]+c[i],f[j]) $
#include<bits/stdc++.h>
using namespace std;
int c[10001],w[10001],f[10001];
int main()
{
int n,m,i,j;
cin>>m>>n;
for(i=1;i<=n;i++)
{
cin>>c[i]>>w[i];
}
for(i=1;i<=n;i++)
{
for(j=w[i];j<=m;j++)
{
f[j]=max(f[j-w[i]]+c[i],f[j]);
}
}
cout<<f[m];
return 0;
}
E.砝码称重
考虑两种解法,一种是暴力(时间复杂度为各砝码数量相乘),一种是背包dp(最坏情况下为\(6*1000^2\));记得初始化,注意输出格式。
暴力:
建议不要采用这种方式,或者加上特判(未写);如果是极限数据,就会被卡
#11
样例输入:65 34 34 23 23 21
样例输出:1000
#include<bits/stdc++.h>
using namespace std;
int c[1001];
int main()
{
int a[7],b[7],i,sum,ans=0;
cin>>a[1]>>a[2]>>a[3]>>a[4]>>a[5]>>a[6];
for(b[1]=0;b[1]<=a[1];b[1]++)
{
for(b[2]=0;b[2]<=a[2];b[2]++)
{
for(b[3]=0;b[3]<=a[3];b[3]++)
{
for(b[4]=0;b[4]<=a[4];b[4]++)
{
for(b[5]=0;b[5]<=a[5];b[5]++)
{
for(b[6]=0;b[6]<=a[6];b[6]++)
{
sum=b[1]+b[2]*2+b[3]*3+b[4]*5+b[5]*10+b[6]*20;
if(c[sum]==0)
{
c[sum]=1;
ans++;
}
}
}
}
}
}
}
ans--;
cout<<"Total="<<ans;
return 0;
}
背包dp:
思路是枚举每个砝码数量,若\(f[k]\)存在,则\(f[k+b[i]]\)一定存在;如果数据范围较大,可将f数组的类型从int改为bool。
#include<bits/stdc++.h>
using namespace std;
int a[7],b[7]={0,1,2,3,5,10,20},f[1001];
int main()
{
int i,j,k,ans=0;
for(i=1;i<=6;i++)
{
cin>>a[i];
}
f[0]=1;
for(i=1;i<=6;i++)
{
for(j=1;j<=a[i];j++)
{
for(k=1000;k>=0;k--)
{
if(f[k]==1)
{
if(f[k+b[i]]==0)
{
f[k+b[i]]=1;
ans++;
}
}
}
}
}
cout<<"Total="<<ans;
return 0;
}
F.最小乘车费用

完全背包变形,枚举每种情况下的距离以达到状态转移。
$ 1<=i<=10,i<=j<=n$
$ f[j]=min(f[j-i]+w[i],f[j])$
#include<bits/stdc++.h>
using namespace std;
int f[10001],w[11];
int main()
{
int n,i,j;
memset(f,0x3f3f3f3f,sizeof(f));
f[0]=0;
for(i=1;i<=10;i++)
{
cin>>w[i];
}
cin>>n;
for(i=1;i<=10;i++)
{
for(j=i;j<=n;j++)
{
f[j]=min(f[j-i]+w[i],f[j]);//用f[j-i]+w[i]和f[j]取最小值更新f[j].f[j-i]表示到j-i的最短花费,加上w[i]即可(走i公里需要的花费)。
}
}
cout<<f[n];
return 0;
}
G.逃亡的准备

初看到这道题以为是混合背包板子,然后就TLE了。(小声bb:我还以为是测评姬波动,前前后后交了170+次)
后来考2022CSP前,将混合背包的01背包和多重背包进行了二进制优化,于是AC。(如果数据再ex亿点,可能要用单调队列优化)
\(1<=i<=n\)
\(if(p[i]==0)\) $ v[i]<=j<=m$ \(f[j]=max(f[j],f[j-v[i]]+w[i])\)
\(else\) 详见代码
#include <bits/stdc++.h>
using namespace std;
int f[10001],v[10001],w[10001],p[10001];
int main()
{
int n,m,i,j,k;
cin>>n>>m;
for(i=1;i<=n;i++)
{
cin>>v[i]>>w[i]>>p[i];
if(p[i]==0)//完全背包
{
for(j=v[i];j<=m;j++)
{
f[j]=max(f[j],f[j-v[i]]+w[i]);
}
}
else//01背包可以当作多重背包来处理
{
for(k=1;k<=p[i];k*=2)
{
for(j=m;j>=k*v[i];j--)
{
f[j]=max(f[j],f[j-k*v[i]]+k*w[i]);
}
p[i]-=k;
}
if(p[i]>0)
{
for(j=m;j>=p[i]*v[i];j--)
{
f[j]=max(f[j],f[j-p[i]*v[i]]+p[i]*w[i]);
}
}
}
}
cout<<f[m]<<endl;
return 0;
}
H.庆功会
不加优化的多重背包即可,优化详见G。
\(1<=i<=n,m>=j>=0,0<=k<=num[i]\)
$ if(j-k*w[i]>=0)$ \(f[j]=max(f[j],f[j-k*w[i]]+k*c[i])\)
#include<bits/stdc++.h>
using namespace std;
int w[10001],c[10001],num[10001],f[10001];
int main()
{
int m,n,i,j,k;
cin>>n>>m;
for(i=1;i<=n;i++)
{
cin>>w[i]>>c[i]>>num[i];
}
for(i=1;i<=n;i++)
{
for(j=m;j>=0;j--)
{
for(k=0;k<=num[i];k++)
{
if(j-k*w[i]>=0)//很重要,避免RE
{
f[j]=max(f[j],f[j-k*w[i]]+k*c[i]);
}
}
}
}
cout<<f[m]<<endl;
return 0;
}
I.混合背包
不加优化的混合背包即可,优化详见G。
\(1<=i<=n\)
\(if(num[i]==1)\) $ m>=j>=w[j]$ \(f[j]=max(f[j],f[j-w[i]]+c[i])\)
\(if(num[i]==0)\) $ w[i]<=j<=m$ \(f[j]=max(f[j],f[j-w[i]]+c[i])\)
\(if(num[i]>1)\) \(m>=j>=0,0<=k<=num[i]\) \(if(j-k*w[i]>=0)\) \(f[j]=max(f[j],f[j-k*w[i]]+k*c[i])\)
#include<bits/stdc++.h>
using namespace std;
int w[10001],c[10001],num[10001],f[10001];
int main()
{
int m,n,i,j,k;
cin>>m>>n;
for(i=1;i<=n;i++)
{
cin>>w[i]>>c[i]>>num[i];
}
for(i=1;i<=n;i++)
{
if(num[i]==1)
{
for(j=m;j>=w[i];j--)
{
f[j]=max(f[j],f[j-w[i]]+c[i]);
}
}
else
{
if(num[i]==0)
{
for(j=w[i];j<=m;j++)
{
f[j]=max(f[j],f[j-w[i]]+c[i]);
}
}
else
{
for(j=m;j>=0;j--)
{
for(k=0;k<=num[i];k++)
{
if(j-k*w[i]>=0)
{
f[j]=max(f[j],f[j-k*w[i]]+k*c[i]);
}
}
}
}
}
}
cout<<f[m]<<endl;
return 0;
}
J.NASA的食物计划
二维费用的背包板子。(教练说非常简单)
\(1<=i<=q,p>=j>=w[i],q>=k>=v[i]\) \(f[j][k]=max(f[j][k],f[j-w[i]][k-v[i]]+c[i])\)
#include<bits/stdc++.h>
using namespace std;
int w[401],v[401],c[401],f[401][401];
int main()
{
int p,q,n,i,j,k;
cin>>p>>q>>n;
for(i=1;i<=n;i++)
{
cin>>w[i]>>v[i]>>c[i];
}
for(i=1;i<=q;i++)
{
for(j=p;j>=w[i];j--)
{
for(k=q;k>=v[i];k--)
{
f[j][k]=max(f[j][k],f[j-w[i]][k-v[i]]+c[i]);
}
}
}
cout<<f[p][q];
return 0;
}
K. 分组背包
分组背包板子。
每组物品有若干种策略:选本组的某一件,或者一件也不选。即设 \(f[k][j]\) 表示前 \(k\) 组物品花费费用 \(j\) 能取得的最大权值,则有$ f[k][j]=max(f[k-1][j],f[k-1][j-w[i]+c[i]|物品i属于第k组)$。接着将其用滚动数组优化即可(转换为一维数组)。
\(1<=k<=t,v>=j>=0,1<=i<=group[k][0]\)
\(if(j>=w[group[i][k]]) f[j]=max(f[j],f[j-w[group[i][k]]]+c[group[i][k]])\)
#include<bits/stdc++.h>
using namespace std;
int group[1001][1001],w[1001],c[1001],f[1001];
int main()
{
int v,n,t,i,j,k,p,q;
cin>>v>>n>>t;
for(i=1;i<=n;i++)
{
cin>>w[i]>>c[i]>>p;
group[p][0]++;
group[p][group[p][0]]=i;
}
for(i=1;i<=t;i++)
{
for(j=v;j>=0;j--)
{
for(k=1;k<=group[i][0];k++)
{
q=group[i][k];
if(j>=w[q])
{
f[j]=max(f[j],f[j-w[q]]+c[q]);
}
}
}
}
cout<<f[v]<<endl;
return 0;
}
L.新年趣事之打牌
01背包的变形,判断是否有解及多组解,同时进行记录,方便后面输出。
#include<bits/stdc++.h>
using namespace std;
int w[100001],s[100001],f[100001],ans[100001];
int main()
{
int n,num,i,j,sum;
cin>>num>>n;
for(i=1;i<=n;i++)
{
cin>>w[i];
}
s[0]=1;//初始化
for(i=1;i<=n;i++)//01背包
{
for(j=num;j>=w[i];j--)
{
if(s[j-w[i]]!=0)
{
s[j]+=s[j-w[i]];
if(f[j]==0)
{
f[j]=i;//初始化+记录路径
}
}
}
}
if(s[num]==0)//无解
{
cout<<"0";
}
if(s[num]==1)//有解
{
sum=num;
while(sum>0)//记录路径(纸牌)
{
ans[f[sum]]=1;
sum-=w[f[sum]];
}
for(i=1;i<=n;++i)//输出
{
if(ans[i]==0)
{
cout<<i<<" ";
}
}
}
if(s[num]>1)//有多组解
{
cout<<"-1";
}
return 0;
}
M. 积木城堡
01背包变形,判断是否有解。
#include<bits/stdc++.h>
using namespace std;
int h[10001],num[10001],f[10001];
int main()
{
int n,ans=0x3f3f3f3f,sum,i,j,k,p,flag=0;//初始化
cin>>n;
for(i=1;i<=n;i++)
{
memset(f,0,sizeof(f));//每次循环都要清空
memset(h,0,sizeof(h));//每次循环都要清空(这条语句可要可不要,但还是带上的好)
f[0]=1;//初始化
p=1;
sum=0;
while(cin>>h[p])
{
if(h[p]==-1)
{
break;
}
else
{
sum+=h[p];
}
p++;
}
p--;//因为读入结束后,p会比实际长度多1,故p--
ans=min(ans,sum);//ans为查询的最大值
for(j=1;j<=p;j++)//01背包
{
for(k=sum;k>=h[j];k--)
{
if(f[k-h[j]]==1)
{
f[k]=1;
}
}
}
for(j=sum;j>=1;j--)//标记下
{
if(f[j]==1)
{
num[j]++;
}
}
}
for(i=ans;i>=1;i--)
{
if(num[i]==n)//有解
{
cout<<i;
flag=1;
break;
}
}
if(flag==0)//无解
{
cout<<"0";
}
return 0;
}

洛谷:
注意数据范围有亿点大,不能用正常的01背包解决。换一种方式,让f数组用来储存容量,即\(f[i]\)用来储存价值为 \(i\) 时所需的最小容量。
$ 1<=i<=t,1000*t>=j>=c[i]$
\(f[j]=max(f[j],f[j-c[i]]+w[i])\)
#include<bits/stdc++.h>
using namespace std;
int w[1000001],c[1000001],f[1000001];
int main()
{
int t,m,i,j;
cin>>t>>m;
memset(f,0x3f,sizeof(f));
for(i=1;i<=t;i++)
{
cin>>w[i]>>c[i];
}
f[0]=0;
for(i=1;i<=t;i++)
{
for(j=1000*t;j>=c[i];j--)
{
f[j]=min(f[j],f[j-c[i]]+w[i]);
}
}
for(i=1000*t;i>0;i--)
{
if(f[i]<=m)
{
cout<<i;
break;
}
}
return 0;
}
单调队列优化多重背包板子
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define endl '\n'
int f[100001],g[100001],w[100001],v[100001],p[100001];
int main()
{
int n,m,i,j,k;
cin>>n>>m;
for(i=1;i<=n;i++)
{
cin>>v[i]>>w[i]>>p[i];
if(p[i]==0)
{
for(j=w[i];j<=m;j++)
{
f[j]=max(f[j],f[j-w[i]]+v[i]);
}
}
else
{
memcpy(g,f,sizeof(f));
for(j=0;j<w[i];j++)
{
deque<int>q;
for(k=0;k<=(m-j)/w[i];k++)
{
if(q.empty()==0&&q.front()<k-p[i])
{
q.pop_front();
}
while(q.empty()==0&&g[j+q.back()*w[i]]-q.back()*v[i]<=g[j+k*w[i]]-k*v[i])
{
q.pop_back();
}
q.push_back(k);
f[j+k*w[i]]=g[j+q.front()*w[i]]+(k-q.front())*v[i];
}
}
}
}
cout<<f[m]<<endl;
return 0;
}
/*
#include <bits/stdc++.h>
using namespace std;
int f[100001],w[100001],v[100001],p[100001];
int main()
{
int n,m,i,j,k;
cin>>n>>m;
for(i=1;i<=n;i++)
{
cin>>v[i]>>w[i]>>p[i];
if(p[i]==0)
{
for(j=w[i];j<=m;j++)
{
f[j]=max(f[j],f[j-w[i]]+v[i]);
}
}
else
{
for(k=1;k<=p[i];k*=2)
{
for(j=m;j>=k*w[i];j--)
{
f[j]=max(f[j],f[j-k*w[i]]+k*v[i]);
}
p[i]-=k;
}
if(p[i]>0)
{
for(j=m;j>=p[i]*w[i];j--)
{
f[j]=max(f[j],f[j-p[i]*w[i]]+p[i]*v[i]);
}
}
}
}
cout<<f[m]<<endl;
return 0;
}
*/
【若归】背包dp做题笔记的更多相关文章
- 状压dp做题笔记
CodeChef Factorial to Square (分块决策) Description 给定一个n,要求在[1,n]中删除一些数,并使剩下的数的乘积是一个完全平方数,同时要求乘积最大,求删除方 ...
- 计数dp做题笔记
YCJS 3924 饼干 Description 给定一个长度为\(n\)的序列,序列每个元素的取值为\([1,x]\),现在给定\(q\)个区间,求在所有取值方案中,区间最小值的最大值的期望是多少? ...
- SDOI2016 R1做题笔记
SDOI2016 R1做题笔记 经过很久很久的时间,shzr终于做完了SDOI2016一轮的题目. 其实没想到竟然是2016年的题目先做完,因为14年的六个题很早就做了四个了,但是后两个有点开不动.. ...
- SAM 做题笔记(各种技巧,持续更新,SA)
SAM 感性瞎扯. 这里是 SAM 做题笔记. 本来是在一篇随笔里面,然后 Latex 太多加载不过来就分成了两篇. 标 * 的是推荐一做的题目. trick 是我总结的技巧. I. P3804 [模 ...
- C语言程序设计做题笔记之C语言基础知识(下)
C 语言是一种功能强大.简洁的计算机语言,通过它可以编写程序,指挥计算机完成指定的任务.我们可以利用C语言创建程序(即一组指令),并让计算机依指令行 事.并且C是相当灵活的,用于执行计算机程序能完成的 ...
- C语言程序设计做题笔记之C语言基础知识(上)
C语言是一种功能强大.简洁的计算机语言,通过它可以编写程序,指挥计算机完成指定的任务.我们可以利用C语言创建程序(即一组指令),并让计算机依指令行事.并且C是相当灵活的,用于执行计算机程序能完成的几乎 ...
- SDOI2017 R1做题笔记
SDOI2017 R1做题笔记 梦想还是要有的,万一哪天就做完了呢? 也就是说现在还没做完. 哈哈哈我竟然做完了-2019.3.29 20:30
- SDOI2014 R1做题笔记
SDOI2014 R1做题笔记 经过很久很久的时间,shzr又做完了SDOI2014一轮的题目. 但是我不想写做题笔记(
- LCT做题笔记
最近几天打算认真复习LCT,毕竟以前只会板子.正好也可以学点新的用法,这里就用来写做题笔记吧.这个分类比较混乱,主要看感觉,不一定对: 维护森林的LCT 就是最普通,最一般那种的LCT啦.这类题目往往 ...
- java做题笔记
java做题笔记 1. 初始化过程是这样的: 1.首先,初始化父类中的静态成员变量和静态代码块,按照在程序中出现的顺序初始化: 2.然后,初始化子类中的静态成员变量和静态代码块,按照在程序中出现的顺序 ...
随机推荐
- MySQL 覆盖索引详解
本文转载自:MySQL 覆盖索引详解,作者 Sevn 1. 什么是索引? 索引(在 MySQL 中也叫"键key")是存储引擎快速找到记录的一种数据结构,通俗来说类似书本的目录. ...
- 基于java+springboot的家教预约网站、家教信息管理系统
该系统是基于java+springboot开发的家教预约网站.是给师妹开发的实习作品.学习过程中,遇到问题可以在github咨询作者. 演示地址 前台地址: http://jiajiao.gitapp ...
- 2023-SWPU NSS秋季招新赛(校外赛道)Misc—我要成为原神高手WP
1.题目信息 我是神里绫华的狗!!! 2.解题方法 有个genshin.h文件夹,打开看看发现里面是一堆文件夹0 1A 1A0等等,而且每个文件夹里面都有文件,0 1A 1A0...看着很眼熟,我们用 ...
- Vue2 - 配置跨域
在根目录下创建 vue.config.js 文件 . 即可 vue.config.js : // vue.config.js 配置说明 //官方vue.config.js 参考文档 https://c ...
- js - 使用 scroll属性手撸轮播图 —— 无缝连接,更丝滑
上效果图: 上代码: <!DOCTYPE html> <html lang="en"> <head> <meta charset= ...
- 微信小程序——如何获取url中的数据
如何获取url中的数据 这里举个例子! 当我们需要编辑一个可以点击的图片超链接 这时候结构是这个样子的 首先最外层有一个视图层view 在view中添加超链接标签<navigator> & ...
- [转帖]MySQL 官方出品,比 mydumper 更快的多线程逻辑备份工具-MySQL Shell Dump & Load
MySQL 官方出品,比 mydumper 更快的多线程逻辑备份工具-MySQL Shell Dump & Load - 知乎 (zhihu.com) 目录 收起 什么是 MySQL Sh ...
- 【转帖】bpftrace 指南
文章目录 0. bpftrace 0.1 bpftrace组件 0.2 bpftrace 帮助信息 0.3 bpftrace 工具速览表 0.4 bpftrace 探针 0.4.1 tracepoin ...
- [转帖]python读取配置文件获取所有键值对_python总结——处理配置文件(ConfigParser)
python处理ConfigParser 使用ConfigParser模块读写ini文件 (转载) ConfigParserPython 的ConfigParser Module中定义了3个类对INI ...
- [转帖]Xargs用法详解
https://www.cnblogs.com/cheyunhua/p/8796433.html 1. 简介 之所以能用到这个命令,关键是由于很多命令不支持|管道来传递参数,而日常工作中有有这个必要, ...