题目链接

P1048 [NOIP2005 普及组] 采药

题目描述

image.png

实现代码

思路:每一个草药都有两种选择:采或者不采,因此可以通过DFS的形式对这两种选择进行比较去最优解;

采用DFS的形式实现:

  1. import java.util.Scanner;
  2. /**
  3. * @Description:
  4. * @Author: JreamY
  5. * @Date: 2022/2/25
  6. **/
  7. public class Demo {
  8. static int result = 0;
  9. public static void main(String[] args) {
  10. Scanner in = new Scanner(System.in);
  11. int T = in.nextInt(); // 总采药时间
  12. int M = in.nextInt(); // 草药总数
  13. int[] times = new int[M + 1];
  14. int[] values = new int[M + 1];
  15. for (int i = 1; i <= M; i++) {
  16. times[i] = in.nextInt();
  17. values[i] = in.nextInt();
  18. }
  19. in.close();
  20. dfs(1, T, M, times, values, 0);
  21. System.out.println(result);
  22. }
  23. public static void dfs(int pos, int T, int M, int[] times, int[] values, int res) {
  24. if (pos == M + 1) {
  25. result = (result > res ? result : res);
  26. return;
  27. }
  28. if (T < 0) {
  29. return;
  30. }
  31. // pos位置的药采
  32. dfs(pos + 1, T, M, times, values, res);
  33. // pos位置的药不采
  34. dfs(pos + 1, T - times[pos], M, times, values, res + values[pos]);
  35. }
  36. }

进一步简化dfs,使其变成有返回的方法:

  1. package ccnu.agile.jdk8;
  2. import java.util.Scanner;
  3. /**
  4. * @Description:
  5. * @Author: JreamY
  6. * @Date: 2022/2/25
  7. **/
  8. public class Demo {
  9. static int result = 0;
  10. public static void main(String[] args) {
  11. Scanner in = new Scanner(System.in);
  12. int T = in.nextInt(); // 总采药时间
  13. int M = in.nextInt(); // 草药总数
  14. int[] times = new int[M + 1];
  15. int[] values = new int[M + 1];
  16. for (int i = 1; i <= M; i++) {
  17. times[i] = in.nextInt();
  18. values[i] = in.nextInt();
  19. }
  20. in.close();
  21. System.out.println(dfs2(1, T, M, times, values));
  22. }
  23. public static int dfs2(int pos, int T, int M, int[] times, int[] values) {
  24. if (pos == M + 1) {
  25. return 0;
  26. }
  27. int unUseResult, useResult = Integer.MIN_VALUE;
  28. unUseResult = dfs2(pos + 1, T, M, times, values);
  29. if (T >= times[pos]) {
  30. useResult = dfs2(pos + 1, T - times[pos], M, times, values) + values[pos];
  31. }
  32. return Math.max(useResult, unUseResult);
  33. }
  34. }

在dfs递归过程中做了很多重复的dfs操作,因此可以通过标记将已经做过操作的保存起来,查询条件为:pos + T:

  1. package ccnu.agile.jdk8;
  2. import java.util.Scanner;
  3. /**
  4. * @Description:
  5. * @Author: JreamY
  6. * @Date: 2022/2/25
  7. **/
  8. public class Demo {
  9. static int result = 0;
  10. public static void main(String[] args) {
  11. Scanner in = new Scanner(System.in);
  12. int T = in.nextInt(); // 总采药时间
  13. int M = in.nextInt(); // 草药总数
  14. int[] times = new int[M + 1];
  15. int[] values = new int[M + 1];
  16. for (int i = 1; i <= M; i++) {
  17. times[i] = in.nextInt();
  18. values[i] = in.nextInt();
  19. }
  20. in.close();
  21. int[][] mem = new int[M+2][T+1];
  22. for(int i=1; i<=M+1; i++) {
  23. for(int j=1; j<=T; j++) {
  24. mem[i][j] = -1;
  25. }
  26. }
  27. System.out.println(dfs3(1, T, M, times, values, mem));
  28. }
  29. public static int dfs3(int pos, int T, int M, int[] times, int[] values, int[][] mem) {
  30. if(mem[pos][T] != -1) {
  31. return mem[pos][T];
  32. }
  33. if (pos == M + 1) {
  34. return mem[pos][T] = 0;
  35. }
  36. int unUseResult, useResult = Integer.MIN_VALUE;
  37. unUseResult = dfs3(pos + 1, T, M, times, values, mem);
  38. if (T >= times[pos]) {
  39. useResult = dfs3(pos + 1, T - times[pos], M, times, values, mem) + values[pos];
  40. }
  41. return mem[pos][T] = Math.max(useResult, unUseResult);
  42. }
  43. }

进一步的来看,可以发现,每一步的结果可以通过已经得到的结果进行表示,即DP,对应的状态转移方程为:

mem[pos][T] = max(mem[pos+1][T], mem[pos+1][T-times[pos]] + values[pos]);