package class012; import java.util.HashMap; public class Code01_MoneyProblem { public static long func1(int[] d, int[] p) { return process(d, p, 0, 0); } // int[] d d[i]:i号怪兽的武力 // int[] p p[i]:i号怪兽要求的钱 // hp 当前你所具有的能力 // index 来到了第index个怪兽的面前 // 目前,你的能力是hp,你来到了index号怪兽的面前,如果要通过后续所有的怪兽, // 请返回需要花的最少钱数 public static long process(int[] d, int[] p, int hp, int index) { if (index == d.length) { // base case return 0; } // index < d.length 还有怪兽要面对 if (hp < d[index]) { return p[index] + process(d, p, hp + d[index], index + 1); } else { // hp >= d[index] 可以贿赂,也可以不贿赂 return Math.min(p[index] + process(d, p, hp + d[index], index + 1), process(d, p, hp, index + 1)); } } // 正数数组 d p public static int dp1(int[] d, int[] p) { if (d == null || d.length == 0) { return 0; } int sum = 0; for (int ability : d) { sum += ability; } int N = d.length; int[][] dp = new int[N + 1][sum + 1]; // dp[N][...] = 0; for (int i = N - 1; i >= 0; i--) { for (int j = 0; j <= sum; j++) { // j 能力 i 怪兽号 if (j + d[i] > sum) { continue; } if (j < d[i]) { dp[i][j] = p[i] + dp[i + 1][j + d[i]]; } else { dp[i][j] = Math.min(p[i] + dp[i + 1][j + d[i]], dp[i + 1][j]); } } } return dp[0][0]; } public static long func2(int[] d, int[] p) { int sum = 0; for (int num : d) { sum += num; } long[][] dp = new long[d.length + 1][sum + 1]; for (int i = 0; i <= sum; i++) { dp[0][i] = 0; } for (int cur = d.length - 1; cur >= 0; cur--) { for (int hp = 0; hp <= sum; hp++) { // 如果这种情况发生,那么这个hp必然是递归过程中不会出现的状态 // 既然动态规划是尝试过程的优化,尝试过程碰不到的状态,不必计算 if (hp + d[cur] > sum) { continue; } if (hp < d[cur]) { dp[cur][hp] = p[cur] + dp[cur + 1][hp + d[cur]]; } else { dp[cur][hp] = Math.min(p[cur] + dp[cur + 1][hp + d[cur]], dp[cur + 1][hp]); } } } return dp[0][0]; } public static long func1dp(int[] d, int[] p) { HashMap dp = new HashMap<>(); return processdp(d, p, 0, 0, dp); } // int[] d d[i]:i号怪兽的武力 // int[] p p[i]:i号怪兽要求的钱 // hp 当前你所具有的能力 // index 来到了第index个怪兽的面前 // 目前,你的能力是hp,你来到了index号怪兽的面前,如果要通过后续所有的怪兽, // 请返回需要花的最少钱数 public static long processdp(int[] d, int[] p, int hp, int index,HashMap dp) { String key = String.valueOf(hp) + "_" + String.valueOf(index); if(dp.containsKey(key)) { return dp.get(key); } long ans = 0; if(index < d.length) { if (hp < d[index]) { ans = p[index] + process(d, p, hp + d[index], index + 1); } else { // hp >= d[index] 可以贿赂,也可以不贿赂 ans = Math.min(p[index] + process(d, p, hp + d[index], index + 1), process(d, p, hp, index + 1)); } } dp.put(key, ans); return ans; } public static long func3(int[] d, int[] p) { int sum = 0; for (int num : p) { sum += num; } // dp[i][j]含义: // 能经过0~i的怪兽,且花钱为j(花钱的严格等于j)时的武力值最大是多少? // 如果dp[i][j]==-1,表示经过0~i的怪兽,花钱为j是无法通过的,或者之前的钱怎么组合也得不到正好为j的钱数 int[][] dp = new int[d.length][sum + 1]; for (int i = 0; i < dp.length; i++) { for (int j = 0; j <= sum; j++) { dp[i][j] = -1; } } // 经过0~i的怪兽,花钱数一定为p[0],达到武力值d[0]的地步。其他第0行的状态一律是无效的 dp[0][p[0]] = d[0]; for (int i = 1; i < d.length; i++) { for (int j = 0; j <= sum; j++) { // 可能性一,为当前怪兽花钱 // 存在条件: // j - p[i]要不越界,并且在钱数为j - p[i]时,要能通过0~i-1的怪兽,并且钱数组合是有效的。 if (j >= p[i] && dp[i - 1][j - p[i]] != -1) { dp[i][j] = dp[i - 1][j - p[i]] + d[i]; } // 可能性二,不为当前怪兽花钱 // 存在条件: // 0~i-1怪兽在花钱为j的情况下,能保证通过当前i位置的怪兽 if (dp[i - 1][j] >= d[i]) { // 两种可能性中,选武力值最大的 dp[i][j] = Math.max(dp[i][j], dp[i - 1][j]); } } } int ans = 0; // dp表最后一行上,dp[N-1][j]代表: // 能经过0~N-1的怪兽,且花钱为j(花钱的严格等于j)时的武力值最大是多少? // 那么最后一行上,最左侧的不为-1的列数(j),就是答案 for (int j = 0; j <= sum; j++) { if (dp[d.length - 1][j] != -1) { ans = j; break; } } return ans; } public static int[][] generateTwoRandomArray(int len, int value) { int size = (int) (Math.random() * len) + 1; int[][] arrs = new int[2][size]; for (int i = 0; i < size; i++) { arrs[0][i] = (int) (Math.random() * value) + 1; arrs[1][i] = (int) (Math.random() * value) + 1; } return arrs; } public static void main(String[] args) { int len = 10; int value = 20; int testTimes = 1000000; for (int i = 0; i < testTimes; i++) { int[][] arrs = generateTwoRandomArray(len, value); int[] d = arrs[0]; int[] p = arrs[1]; long ans1 = func1(d, p); long ans2 = func2(d, p); long ans3 = func3(d, p); if (ans1 != ans2 || ans2 != ans3) { System.out.println("oops!"); } } } }