第五周的题

本文介绍了编程中的背包问题实例,包括01背包(洛谷P1048)、完全背包(P1616),以及多重背包(P1049和P1833)的解题思路和C++代码实现。通过动态规划方法找到在给定时间内获取最大价值的策略。
摘要由CSDN通过智能技术生成

本周的题主要是背包入门问题,涉及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;
}

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值