tyvj P1005 采药

本文介绍了一个经典的背包问题——草药采集问题,并详细解释了如何利用动态规划解决此问题,包括关键步骤和实现代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

-题目来源:

题目描述:

描述

辰辰是个天资聪颖的孩子,他的梦想是成为世界上最伟大的医师。为此,他想拜附近最有威望的医师为师。医师为了判断他的资质,给他出了一个难题。医师把他带到一个到处都是草药的山洞里对他说:“孩子,这个山洞里有一些不同的草药,采每一株都需要一些时间,每一株也有它自身的价值。我会给你一段时间,在这段时间里,你可以采到一些草药。如果你是一个聪明的孩子,你应该可以让采到的草药的总价值最大。” 如果你是辰辰,你能完成这个任务吗?

输入格式

输入文件medic.in的第一行有两个整数T(1 <= T <= 1000)和M(1 <= M <= 100),用一个空格隔开,T代表总共能够用来采药的时间,M代表山洞里的草药的数目。接下来的M行每行包括两个在1到100之间(包括1和100)的整数,分别表示采摘某株草药的时间和这株草药的价值。

输出格式

输出文件medic.out包括一行,这一行只包含一个整数,表示在规定的时间内,可以采到的草药的最大总价值。

测试样例1

输入

70 3 71 100 69 1 1 2

输出

3

备注

对于30%的数据,M <= 10;对于全部的数据,M <= 100。
-------------------------------------------------------------------------------------------------------------------------------------
思路:这个问题属于背包问题,更进一步讲是0-1问题。
一开始我尝试使用价值/时间最大法,即将所有药材都以 价值/时间 降序排列,从 价值/时间 最大的开始塞进背包直到超出时间。这种方法的确能够通过样例,但是这个样例实在太简单了,没有暴露出其他问题,所以没有AC,于是将原题目百度了一下,发现要使用动态规划的方法。(可见:http://wenku.baidu.com/link?url=SDqMRJ5bPrTPn3P-Nx3dpao2nVqvPiiBghk8qd5CMtDVFp5CWdOSBkrKgpHdStWB7vHPFSU_o3qSBknCh_boul2TXH1vT8ZJhsAy2roEUtq)
关键的动态规划要点我总结如下:
1、把所有的草药从上到下排成1列,从1开始升序编号。
2、采药的终极目标是使到最后的价值最大,那么我们可以求对每一株的最大价值来获得最后的最大价值。
3、两个重要推论:(来自分析和解答)
4、

Y

w

j

>

)

,

1

(

)

,

(

Y

j

A

Y

j

A

=

若wj>Y,则A(j,Y)=A(j-1,Y)。也就是说,如果采这药材的时间超过最长时间,那么到这个药材的最大价值等于前一个药材的最大价值。
5、若wj<Y,则A(j,Y)=max(A(j-1,Y),A(j-1,Y-wj)+pj).这就是说,如果采这个药材的时间没有超过最长时间,那么这株药材的最大价值时间就是前一个药材并且在Y减去该药材所需时间后的局限时间里的最大价值。
6、开二维数组 int Gather[M+1][T+1],Gather[i][j]的值表示总的采药时间不超过j,采药数量不超过i时所采草药的最大价值。
然后以下图的顺序开始遍历
_______________________________________________________________________________________________________________________________________________________
知道思路之后,会遇到一些代码编写上的小问题。(我用的是C)
要注意到Gather二维数组的是以[数量][时间]为坐标的,这和输入数据不同,容易搞混;
其次,Gather二维数组的标度比真正的标度要大1,所以Array数组中会出现i-1的情况。
AC代码如下:
#include <stdio h=""></stdio>
#include <stdio h="">
<pre name="code" class="cpp">#include <stdlib.h>
void Package(int T,int M);
int Max(int A,int B);

int Array[100][2];
int Gather[101][1001];






int main()
{
	int M,T;
	int Time, Result;
	int i;

	scanf("%d%d",&T,&M);

	for(i = 0;i < M;i++)
	{
		scanf("%d%d",&Array[i][0],&Array[i][1]);
	}


	Time=0;
	Result=0;

	Package(T,M);

	

	printf("%d\n",Gather[M][T]);
	//system("pause");
	return 0;

}

void Package(int T,int M)
{
	int i,j;
	int a,b;

	
	for(i=0;i<T+1;i++)
		Gather[0][i]=0;

	for(i=0;i<M+1;i++)
		Gather[i][0]=0;


	for(i = 1;i < T+1;i++)
		for(j = 1;j < M+1;j++)
		{
			if(Array[j-1][0]>i)
				Gather[j][i]=Gather[j-1][i];
			else
			{
				Gather[j][i]=Max(Gather[j-1][i],Gather[j-1][i-Array[j-1][0]]+Array[j-1][1]);
			}

			/*for(a = 0; a < M+1; a++)
			{
				for(b = 0; b <T+1;b++)
					printf("%2d",Gather[a][b]);
				printf("\n");
			}
			printf("__________________________\n");
*/
		}


}

int Max(int A,int B)
{
	return (A>B)?A:B;
}






评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值