本周的题主要是背包入门问题,涉及01背包,完全背包,和多重背包
1、
题目:洛谷P1048 [NOIP2005 普及组] 采药
辰辰是个天资聪颖的孩子,他的梦想是成为世界上最伟大的医师。为此,他想拜附近最有威望的医师为师。医师为了判断他的资质,给他出了一个难题。医师把他带到一个到处都是草药的山洞里对他说:“孩子,这个山洞里有一些不同的草药,采每一株都需要一些时间,每一株也有它自身的价值。我会给你一段时间,在这段时间里,你可以采到一些草药。如果你是一个聪明的孩子,你应该可以让采到的草药的总价值最大。”第一行有 2 个整数 T(1≤T≤1000)和 (1≤M≤100),用一个空格隔开,T 代表总共能够用来采药的时间,M 代表山洞里的草药的数目。接下来的 M 行每行包括两个在 1 到 100 之间(包括 1 和100)的整数,分别表示采摘某株草药的时间和这株草药的价值。输出在规定的时间内可以采到的草药的最大总价值。
思路:
这是一个很明显的01背包问题,对于这个问题,分别用am【】,bm【】储存时间和价值,先开辟一个数组abm[i][j]用来放存在前i个草药,拥有j个时间时所能拿到的最大价值,因此abm【i】【j】=max(abm【i-1】【j】,abm【i-1】【j-am【i】】+bm【i】),最后输出最后一个abm【】【】就是答案。
# include <bits/stdc++.h>
using namespace std;
int main()
{
int t,m; //am体积bm价值
scanf("%d %d",&t,&m);
int am[m+1]={0},bm[m+1]={0},abm[m+1][t+1]={0};
for(int a=1;a<=m;a++) scanf("%d %d",&am[a],&bm[a]);
for(int a=1;a<=m;a++)
{
for(int b=t;b>=0;b--)
{
abm[a][b] = abm[a - 1][b];
if(b>=am[a])
abm[a][b]=max(abm[a-1][b],abm[a-1][b-am[a]]+bm[a]);
}
}
printf("%d",abm[m][t]);
return 0;
}
2、
题目:P1616 疯狂的采药
LiYuxiang 是个天资聪颖的孩子,他的梦想是成为世界上最伟大的医师。为此,他想拜附近最有威望的医师为师。医师为了判断他的资质,给他出了一个难题。医师把他带到一个到处都是草药的山洞里对他说:“孩子,这个山洞里有一些不同种类的草药,采每一种都需要一些时间,每一种也有它自身的价值。我会给你一段时间,在这段时间里,你可以采到一些草药。如果你是一个聪明的孩子,你应该可以让采到的草药的总价值最大。”如果你是 LiYuxiang,你能完成这个任务吗?
此题和原题的不同点:1. 每种草药可以无限制地疯狂采摘。2. 药的种类眼花缭乱,采药时间好长好长啊!师傅等得菊花都谢了!
输入第一行有两个整数,分别代表总共能够用来采药的时间 t 和代表山洞里的草药的数目 m。第 2 到第 (m+1) 行,每行两个整数,第 (i+1) 行的整数 ai,bi 分别表示采摘第 i 种草药的时间和该草药的价值。输出一行,这一行只包含一个整数,表示在规定的时间内,可以采到的草药的最大总价值。
思路:
采药时间长,直接用long long;每种草药无限制,说明是完全背包,直接用一维数组,ddm【】是时间,vl【】是价值,abm【】用来放存在前i个草药,拥有j个时间时所能拿到的最大价值,
abm【j】=max(abm【j】,abm【j-ddm【i】】+vl【i】)完全背包是外面循环是i从1到m(正着来),内层循环是从ddm【i】到t(正着来),输出最后一个abm【】即为答案
# include <bits/stdc++.h>
using namespace std;
int ddm[9999999],vl[9999999];
double abm[9999999]={0};
int main()
{
long long t,m;
scanf("%lld %lld",&t,&m);
for(int a=1;a<=m;a++)
{
scanf("%d %d",&ddm[a],&vl[a]);
}
for(int a=1;a<=m;a++)
{
for(int b=ddm[a];b<=t;b++)
{
abm[b]=max(abm[b],abm[b-ddm[a]]+vl[a]);
}
}
printf("%.0lf",abm[t]);
return 0;
}
3、
题目:P1049 [NOIP2001 普及组] 装箱问题
有一个箱子容量为 V,同时有 n 个物品,每个物品有一个体积。现在从 n 个物品中,任取若干个装入箱内(也可以不取),使箱子的剩余空间最小。输出这个最小值。第一行共一个整数 V,表示箱子容量。第二行共一个整数 n,表示物品总数。接下来 n 行,每行有一个正整数,表示第 i 个物品的体积。共一行一个整数,表示箱子最小剩余空间。
思路:
只要假设物体的体积和物体的价值相等,就会发现求箱子最小体积就是求物体的最大价值,总体积减去最大价值就是剩余最小体积,因此这也是一个01背包的问题,这一次用一维的方式解题,dp【】储存最大价值,num【】储存物体的体积同时也是价值,
dp【j】=max(dp【j】,dp【j-num【i】】+num【i】)01背包是外面循环是i从1到n(正着来),内层循环是从v到num【i】(倒着来),输出v-最后一个dp【】
# include <bits/stdc++.h>
using namespace std;
int main()
{
int v,n;
scanf("%d",&v);
scanf("%d",&n);
int num[n+1]={0},dp[v+1]={0};
for(int a=1;a<=n;a++) scanf("%d",&num[a]);
for(int b=1;b<=n;b++)
{
for(int a=v;a>=num[b];a--)
{
dp[a]=max(dp[a],dp[a-num[b]]+num[b]);
}
}
printf("%d",v-dp[v]);
return 0;
}
4、
题目:P1833 樱花
爱与愁大神后院里种了 n 棵樱花树,每棵都有美学值 Ci(0≤Ci≤200)。爱与愁大神在每天上学前都会来赏花。爱与愁大神可是生物学霸,他懂得如何欣赏樱花:一种樱花树看一遍过,一种樱花树最多看Pi(0≤Pi≤100) 遍,一种樱花树可以看无数遍。但是看每棵樱花树都有一定的时间 Ti(0≤Ti≤100)。爱与愁大神离去上学的时间只剩下一小会儿了。求解看哪几棵樱花树能使美学值最高且爱与愁大神能准时(或提早)去上学。
输入共n+1行:
第 11 行:现在时间 Ts(几时:几分),去上学的时间 Te(几时:几分),爱与愁大神院子里有几棵樱花树 n。这里的 Ts,Te 格式为:hh:mm
,其中 0≤hh≤23,0≤mm≤59,且 hh,mm,n 均为正整数。
第 2 行到第n+1 行,每行三个正整数:看完第 i 棵树的耗费时间 Ti,第 i 棵树的美学值 Ci,看第 i 棵树的次数 Pi(Pi=0 表示无数次,Pi 是其他数字表示最多可看的次数 Pi)。
思路:
很明显是个多重背包的问题,用二进制转化成01背包的问题,先设置数组v表示时间,w表示价值,对于每一个树,都有三组数据,将每一个树的可观看次数分成1,2,4……加到数组中,如果某一个树可以看无数次,等同于可以看t/v【i】次(因为就算时间全用来看这棵树,最多看t/v【i】次),然后正常用一维01背包解即可
# include <bits/stdc++.h>
using namespace std;
int v[100010],w[100010],dp[100010],a,b,m;
int main()
{
int m1,m2,n1,n2,t,n,time=0;
scanf("%d:%d %d:%d %d",&m1,&n1,&m2,&n2,&n);
if(m2>m1) t=60*(m2-m1)+n2-n1;
else t=n2-n1;
for(int i=1;i<=n;i++)
{ //a是时间,b是价值,v是时间,w是价值
scanf("%d %d %d",&a,&b,&m);
if(m==0) m=t/a;
for(int k=1;k<=m;k*=2)
{
time++;
v[time]=k*a;
w[time]=k*b;
m-=k;
}
if(m>0)
{
time++;
v[time]=m*a;
w[time]=m*b;
}
}
for(int i=1;i<=time;i++)
for(int j=t;j>=v[i];j--)
{
dp[j]=max(dp[j],dp[j-v[i]]+w[i]);
}
printf("%d",dp[t]);
return 0;
}