前言:

现在决定未来,未来与过去无关。--波波

前置知识:

dd_engi的背包九讲(新版转载)

|

背包九讲——全篇详细理解与代码实现

背包问题 (附单调队列优化多重背包

|

背包问题入门(单调队列优化多重背包

多重背包问题入门+dp储存空间优化

动态规划——各类背包及优化问题详解

题目来自学校OJ、洛谷、ybtOJ。

AtCoder dp 26 题

背包精选

背包问题(简单)

背包问题

学校OJ:

A.采药

P1048 [NOIP2005 普及组] 采药

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.开心的金明

P1060[NOIP2006 普及组] 开心的金明

与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. 完全背包问题

ybtOJ 1268:【例9.12】完全背包问题

完全背包板子(优化后面讲),因范围较小,选用不加优化的做法。

完全背包板子和和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.砝码称重

P2347 [NOIP1996 提高组] 砝码称重

考虑两种解法,一种是暴力(时间复杂度为各砝码数量相乘),一种是背包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.庆功会

ybtOJ 1269:【例9.13】庆功会

不加优化的多重背包即可,优化详见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.混合背包

ybtOJ 1270:【例9.14】混合背包

不加优化的混合背包即可,优化详见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的食物计划

P1507 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. 分组背包

ybtOJ 1272:【例9.16】分组背包

分组背包板子。

每组物品有若干种策略:选本组的某一件,或者一件也不选。即设 \(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.新年趣事之打牌

Vijos1071 新年趣事之打牌

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. 积木城堡

P1504 积木城堡

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;
}

洛谷:

AT_dp_e Knapsack 2

注意数据范围有亿点大,不能用正常的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;
}

P1776 宝物筛选

单调队列优化多重背包板子

#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做题笔记的更多相关文章

  1. 状压dp做题笔记

    CodeChef Factorial to Square (分块决策) Description 给定一个n,要求在[1,n]中删除一些数,并使剩下的数的乘积是一个完全平方数,同时要求乘积最大,求删除方 ...

  2. 计数dp做题笔记

    YCJS 3924 饼干 Description 给定一个长度为\(n\)的序列,序列每个元素的取值为\([1,x]\),现在给定\(q\)个区间,求在所有取值方案中,区间最小值的最大值的期望是多少? ...

  3. SDOI2016 R1做题笔记

    SDOI2016 R1做题笔记 经过很久很久的时间,shzr终于做完了SDOI2016一轮的题目. 其实没想到竟然是2016年的题目先做完,因为14年的六个题很早就做了四个了,但是后两个有点开不动.. ...

  4. SAM 做题笔记(各种技巧,持续更新,SA)

    SAM 感性瞎扯. 这里是 SAM 做题笔记. 本来是在一篇随笔里面,然后 Latex 太多加载不过来就分成了两篇. 标 * 的是推荐一做的题目. trick 是我总结的技巧. I. P3804 [模 ...

  5. C语言程序设计做题笔记之C语言基础知识(下)

    C 语言是一种功能强大.简洁的计算机语言,通过它可以编写程序,指挥计算机完成指定的任务.我们可以利用C语言创建程序(即一组指令),并让计算机依指令行 事.并且C是相当灵活的,用于执行计算机程序能完成的 ...

  6. C语言程序设计做题笔记之C语言基础知识(上)

    C语言是一种功能强大.简洁的计算机语言,通过它可以编写程序,指挥计算机完成指定的任务.我们可以利用C语言创建程序(即一组指令),并让计算机依指令行事.并且C是相当灵活的,用于执行计算机程序能完成的几乎 ...

  7. SDOI2017 R1做题笔记

    SDOI2017 R1做题笔记 梦想还是要有的,万一哪天就做完了呢? 也就是说现在还没做完. 哈哈哈我竟然做完了-2019.3.29 20:30

  8. SDOI2014 R1做题笔记

    SDOI2014 R1做题笔记 经过很久很久的时间,shzr又做完了SDOI2014一轮的题目. 但是我不想写做题笔记(

  9. LCT做题笔记

    最近几天打算认真复习LCT,毕竟以前只会板子.正好也可以学点新的用法,这里就用来写做题笔记吧.这个分类比较混乱,主要看感觉,不一定对: 维护森林的LCT 就是最普通,最一般那种的LCT啦.这类题目往往 ...

  10. java做题笔记

    java做题笔记 1. 初始化过程是这样的: 1.首先,初始化父类中的静态成员变量和静态代码块,按照在程序中出现的顺序初始化: 2.然后,初始化子类中的静态成员变量和静态代码块,按照在程序中出现的顺序 ...

随机推荐

  1. MySQL 覆盖索引详解

    本文转载自:MySQL 覆盖索引详解,作者 Sevn 1. 什么是索引? 索引(在 MySQL 中也叫"键key")是存储引擎快速找到记录的一种数据结构,通俗来说类似书本的目录. ...

  2. 基于java+springboot的家教预约网站、家教信息管理系统

    该系统是基于java+springboot开发的家教预约网站.是给师妹开发的实习作品.学习过程中,遇到问题可以在github咨询作者. 演示地址 前台地址: http://jiajiao.gitapp ...

  3. 2023-SWPU NSS秋季招新赛(校外赛道)Misc—我要成为原神高手WP

    1.题目信息 我是神里绫华的狗!!! 2.解题方法 有个genshin.h文件夹,打开看看发现里面是一堆文件夹0 1A 1A0等等,而且每个文件夹里面都有文件,0 1A 1A0...看着很眼熟,我们用 ...

  4. Vue2 - 配置跨域

    在根目录下创建 vue.config.js 文件 . 即可 vue.config.js : // vue.config.js 配置说明 //官方vue.config.js 参考文档 https://c ...

  5. js - 使用 scroll属性手撸轮播图 —— 无缝连接,更丝滑

    上效果图: 上代码: <!DOCTYPE html> <html lang="en"> <head>     <meta charset= ...

  6. 微信小程序——如何获取url中的数据

    如何获取url中的数据 这里举个例子! 当我们需要编辑一个可以点击的图片超链接 这时候结构是这个样子的 首先最外层有一个视图层view 在view中添加超链接标签<navigator> & ...

  7. [转帖]MySQL 官方出品,比 mydumper 更快的多线程逻辑备份工具-MySQL Shell Dump & Load

    MySQL 官方出品,比 mydumper 更快的多线程逻辑备份工具-MySQL Shell Dump & Load - 知乎 (zhihu.com) ​ 目录 收起 什么是 MySQL Sh ...

  8. 【转帖】bpftrace 指南

    文章目录 0. bpftrace 0.1 bpftrace组件 0.2 bpftrace 帮助信息 0.3 bpftrace 工具速览表 0.4 bpftrace 探针 0.4.1 tracepoin ...

  9. [转帖]python读取配置文件获取所有键值对_python总结——处理配置文件(ConfigParser)

    python处理ConfigParser 使用ConfigParser模块读写ini文件 (转载) ConfigParserPython 的ConfigParser Module中定义了3个类对INI ...

  10. [转帖]Xargs用法详解

    https://www.cnblogs.com/cheyunhua/p/8796433.html 1. 简介 之所以能用到这个命令,关键是由于很多命令不支持|管道来传递参数,而日常工作中有有这个必要, ...