经典背包题 - 采药

199 阅读3分钟

Offer 驾到,掘友接招!我正在参与2022春招打卡活动,点击查看活动详情

一、题目描述:

这个山洞里有一些不同的草药,采每一株都需要一些时间,每一株也有它自身的价值。我会给你一段时间,在这段时间里,你可以采到一些草药。如果你是一个聪明的孩子,你应该可以让采到的草药的总价值最大。

来源:洛谷 www.luogu.com.cn/problem/P10…

输入格式

第一行有 2 个整数 T(1≤T≤1000)和 M(1≤M≤100),用一个空格隔开,T 代表总共能够用来采药的时间,M 代表山洞里的草药的数目。

接下来的 M 行每行包括两个在 1 到 100 之间(包括 1 和 100)的整数,分别表示采摘某株草药的时间和这株草药的价值。

70 3
71 100
69 1
1 2

输出格式

输出在规定的时间内可以采到的草药的最大总价值。

3

二、思路分析:

很明显这是一个01背包问题( 01背包:一共有N件物品 ; 完全背包 :一共有N种物品, 每种物品可以有无限多个。 多重背包: 一共有N种物品,第i种物品的数量为n[i] )。而01背包的公式F[i,v]=max{F[i-1,v],F[i-1,v-c[i]]+w[i]}

这个公式的意思就是比较采这颗草药和不采这颗草药(去采其他草药)的背包价值哪个最大,就可以给它记忆化存起来。那什么时候不采药呢,例如当该颗采药需要花费的时间很多还没有什么价值。

我们分别来看一下这个公式 F[i,v] 采第i颗草药,花费时间v可以采到的草药的最大总价值, F[i-1,v] 当我们不够时间采摘这颗草药 或者 选择不采摘时的价值,我们只需要继承前i颗草药在时间v内能采摘的最大总价值。 F[i-1,v-c[i]]+w[i] 当我们决定采摘这颗草药时,把采摘这颗草药后剩下的时间给前i颗草药找最大总价值然后加上我们采摘这颗草药的价值就是此次采摘的总价值。 最后比较采摘和不采摘的总价值保存最大的价值即可。

三、AC 代码:

import java.util.*;

public class Main {
	public static void main(String[] args) {
		Scanner sr = new Scanner(System.in);
		int T = sr.nextInt();   // T代表总共能够用来采药的时间
		int M = sr.nextInt();   // M代表山洞里的草药的数目
                // 保存最大价值的表
		int[][] a = new int[M + 1][T + 1];
		int[] t = new int[M + 1];
		int[] v = new int[M + 1];
		for (int i = 1; i <= M; i++) {
			t[i] = sr.nextInt();
			v[i] = sr.nextInt();
		}
		for (int i = 1; i <= M; i++)     // 第几株草药
			for (int j = 1; j <= T; j++) // 有多少时间
			{
                     //  时间不够采摘这颗草药
				if (j < t[i])
					a[i][j] = a[i - 1][j];
				else {
                          // val1 采摘    val2 不采摘
					int val1 = a[i - 1][j - t[i]] + v[i];
					int val2 = a[i - 1][j];
					a[i][j] = val1 > val2 ? val1 : val2;
				}
			}
                // 最后留下的一定是最大值(所有草药都可以选择采 并且时间最多)
		System.out.println(a[M][T]);
	}
}

看一下这个数组 image.png

四、总结:

01背包还有一维数组记录的解法,但是我比较喜欢二维👻,我觉得背包问题主要是要想清楚这个公式的原因吧,然后直接套上去就可以了