前言:

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

前置知识:

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. C# WPF:这次把文件拖出去!

    首发公众号:Dotnet9 作者:沙漠之尽头的狼 编辑于:成都,2020-12-01 回顾上篇文章:C# WPF:把文件给我拖进来!!! 本文完成对应的下文:<C# WPF:这次把文件拖出去!& ...

  2. AI伴侣下载

    总结 现在网页上很多下载的AI伴侣下载下来都会有些问题或者不能用,如下链接下载的AI伴侣亲测可以使用! (连接后会提示更新,博主没有选择更新,如有需要也可以更新) https://mit-ai2-co ...

  3. java - 类属性 初始化的三种形式及顺序

    package chushihua; public class Hello { public static String name = getValue("属性"); { name ...

  4. [转帖]TiUP 命令概览

    https://docs.pingcap.com/zh/tidb/stable/tiup-reference TiUP 在 TiDB 生态中承担包管理器的功能,管理着 TiDB 生态下众多的组件,如 ...

  5. [转帖]Kafka主题与分区

    https://zhuanlan.zhihu.com/p/428845986#:~:text=%E4%B8%80%E3%80%81kafka-topics.sh%E6%93%8D%E4%BD%9C%2 ...

  6. [转帖]Linux系统NVME盘分区和挂载

    https://www.jianshu.com/p/04327f1b97cb 查看系统里面识别到的硬盘和分区的信息 $ sudo fdisk -l Disk /dev/nvme1n1: 1.8 TiB ...

  7. [转帖]Windows自带硬盘测试工具使用教程

    本教程主要讲解Windows自带的硬盘测试工具的使用,不用再安装第三方软件了.到底准不准就不知道啦,下面我们来看看如何使用吧~ 1. 进入cmd 快速进入cmd 主要如果进入后,使用命令直接闪退,就是 ...

  8. Linux 下面删除指定日期之前文件的办法

    1. Linux 下面最近有一个需求 需要只更新2020年4月10号之后补丁的需求 2. rsync 能够拉取所有的补丁文件  没找到能够按照日期进行拉取的办法. 所以想了一个折中的办法 先拉取 再按 ...

  9. 麒麟信安V3.4 安装PG15的过程

    麒麟信安V3.4 安装PG15的过程 背景 发现基于OpenEuler的几个系统使用CentOS的rpm包 安装PG数据库时有问题. 会提示缺少依赖的so文件. 今天想着解决一下, 就百度了一下并且进 ...

  10. 【原创】关于xenomai3 RTnet的一点记录

    xenomai3协议栈RTnet支持TCP.UDP,但不支持IGMP: 对ARP支持有限制:地址解析的延迟会影响数据包传输延迟,RTnet为实时性考虑,路由表设计静态的,只在设置期间配置,或者接收到其 ...