You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

247 lines
8.2 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

package class_2022_05_4_week;
// 来自弗吉尼亚理工大学(VT),算法考试卷
// 精选了还可以的几道题
// 这些都是简单难度的动态规划,是面试中最常见的难度
// 这几个题都有一些非常小的常见技巧可说
public class Code01_SomeDPFromVT {
// // arr[i][0] : 有趣值
// // arr[i][1] : 进攻值
// // arr[index...]所有的方案自由选择
// // 必须让restFunny、restOffense值 <= 0
// // 返回最小的方案数量(index...)
// public static int process(int[][] arr, int index, int restFunny, int restOffense) {
// if (restFunny <= 0 && restOffense <= 0) {
// return 0;
// }
// // 有的值,还没扣完
// if (index == arr.length) {
// return Integer.MAX_VALUE; // 无效值
// }
// // 有的值还没扣完 但是还有方案可选
// // index号方案 不要 要
// int p1 = process(arr, index + 1, restFunny, restOffense);
// // 要用index号方案
// int p2 = Integer.MAX_VALUE;
// int next = process(arr, index + 1, restFunny - arr[index][0], restOffense - arr[index][1]);
// if (next != Integer.MAX_VALUE) {
// p2 = 1 + next;
// }
// return Math.min(p1, p2);
// }
// 题目1
// 方案1 : {7, 10}
// xxxx : {a , b}
// 1 2 3 4
// FunnyGoal = 100
// OffenseGoal = 130
// 找到一个最少方案数让FunnyGoal、OffenseGoal都大于等于
// 定义如下尝试过程
// 贴纸数组stickers
// stickers[i][0] : i号贴纸的Funny值
// stickers[i][1] : i号贴纸的Offense值
// index....所有的贴纸随便选择。index之前的贴纸不能选择
// 在让restFunny和restOffense都小于等于0的要求下返回最少的贴纸数量
public static int minStickers1(int[][] stickers, int funnyGoal, int offenseGoal) {
return process1(stickers, 0, funnyGoal, offenseGoal);
}
public static int process1(int[][] stickers, int index, int restFunny, int restOffense) {
if (restFunny <= 0 && restOffense <= 0) {
return 0;
}
if (index == stickers.length) {
return Integer.MAX_VALUE;
}
// 不选当前的贴纸
int p1 = process1(stickers, index + 1, restFunny, restOffense);
// 选当前贴纸
int p2 = Integer.MAX_VALUE;
int next2 = process1(stickers, index + 1, Math.max(0, restFunny - stickers[index][0]),
Math.max(0, restOffense - stickers[index][1]));
if (next2 != Integer.MAX_VALUE) {
p2 = next2 + 1;
}
return Math.min(p1, p2);
}
// 改动态规划
public static int minStickers2(int[][] stickers, int funnyGoal, int offenseGoal) {
int[][][] dp = new int[stickers.length][funnyGoal + 1][offenseGoal + 1];
for (int i = 0; i < stickers.length; i++) {
for (int j = 0; j <= funnyGoal; j++) {
for (int k = 0; k <= offenseGoal; k++) {
dp[i][j][k] = -1;
}
}
}
return process2(stickers, 0, funnyGoal, offenseGoal, dp);
}
public static int process2(int[][] stickers, int index, int restFunny, int restOffense, int[][][] dp) {
if (restFunny <= 0 && restOffense <= 0) {
return 0;
}
if (index == stickers.length) {
return Integer.MAX_VALUE;
}
if (dp[index][restFunny][restOffense] != -1) {
return dp[index][restFunny][restOffense];
}
// 不选当前的贴纸
int p1 = process2(stickers, index + 1, restFunny, restOffense, dp);
// 选当前贴纸
int p2 = Integer.MAX_VALUE;
int next2 = process2(stickers, index + 1, Math.max(0, restFunny - stickers[index][0]),
Math.max(0, restOffense - stickers[index][1]), dp);
if (next2 != Integer.MAX_VALUE) {
p2 = next2 + 1;
}
int ans = Math.min(p1, p2);
dp[index][restFunny][restOffense] = ans;
return ans;
}
// 严格位置依赖的动态规划
public static int minStickers3(int[][] stickers, int funnyGoal, int offenseGoal) {
int n = stickers.length;
int[][][] dp = new int[n + 1][funnyGoal + 1][offenseGoal + 1];
for (int f = 0; f <= funnyGoal; f++) {
for (int o = 0; o <= offenseGoal; o++) {
if (f != 0 || o != 0) {
dp[n][f][o] = Integer.MAX_VALUE;
}
}
}
for (int i = n - 1; i >= 0; i--) {
for (int f = 0; f <= funnyGoal; f++) {
for (int o = 0; o <= offenseGoal; o++) {
if (f != 0 || o != 0) {
int p1 = dp[i + 1][f][o];
int p2 = Integer.MAX_VALUE;
int next2 = dp[i + 1][Math.max(0, f - stickers[i][0])][Math.max(0, o - stickers[i][1])];
if (next2 != Integer.MAX_VALUE) {
p2 = next2 + 1;
}
dp[i][f][o] = Math.min(p1, p2);
}
}
}
}
return dp[0][funnyGoal][offenseGoal];
}
// 题目2
// 绳子总长度为M
// 100 -> M
// (6, 100) (7,23) (10,34) -> arr
// 每一个长度的绳子对应一个价格,比如(6, 10)表示剪成长度为6的绳子对应价格10
// 可以重复切出某个长度的绳子
// 定义递归如下:
// 所有可以切出来的长度 对应 价值都在数组ropes里
// ropes[i] = {6, 10} 代表i方案为切出长度为6的绳子可以卖10元
// index....所有的方案随便选择。index之前的方案不能选择
// 返回最大的价值
// 自己去改动态规划
// arr[i][0] -> i号方案能切多少长度
// arr[i][1] -> 切出来这个长度,就能获得的价值
// arr[index....]自由选择绳子还剩restLen长度
// 返回,最大价值
public static int maxValue(int[][] arr, int index, int restLen) {
if (restLen <= 0 || index == arr.length) {
return 0;
}
// 绳子还有剩余、且还有方案
// index号方案
// 不选
int p1 = maxValue(arr, index + 1, restLen);
// 选
int p2 = 0;
if (arr[index][0] <= restLen) { // 剩余绳子够长,才能选当前方案
p2 = arr[index][1] + maxValue(arr, index, restLen - arr[index][0]);
}
return Math.max(p1, p2);
}
// public static int maxValue(int[][] ropes, int index, int restLen) {
// if (restLen <= 0) {
// return 0;
// }
// // 当前index方案就是不考虑
// int p1 = maxValue(ropes, index + 1, restLen);
// // 当前index方案考虑然后因为可以重复选所以注意下面的子过程调用
// int p2 = -1;
// if (ropes[index][0] <= restLen) {
// // 当前index方案选了一份
// // 但是下面依然可以重复的选index方案
// // 所以子过程里的index不增加只是剩余长度减少
// p2 = ropes[index][1] + maxValue(ropes, index, restLen - ropes[index][0]);
// }
// return Math.max(p1, p2);
// }
// 题目3
// 每一个序列都是[a,b]的形式a < b
// 序列连接的方式为前一个序列的b要等于后一个序列的a
// 比如 : [3, 7]、[7, 13]、[13, 26]这三个序列就可以依次连接
// 给定若干个序列,求最大连接的数量
// 定义尝试过程如下
// arr[i] = {4, 9}表示第i个序列4开始9结束
// pre : 代表选择的上一个序列index是多少
// 比如选择的上一个序列如果是(4,9)是第5个序列那么pre==5
// 特别注意如果从来没有选过序列那么pre == -1
// 这个函数含义 :
// index....所有的序列随便选择。index之前的序列不能选择
// 上一个选择的序列是pre号如果pre==-1,说明之前没有选择过序列
// 返回题目要求的那种连接方式下,最大的序列数量
// [5,13] [1,19] [2, 3] [79, 81] ...
// [1,19] [2, 3] [5, 13] [79, 81]
// arr[i][0] : 开头
// arr[i][1] : 结尾
// arr已经根据开头排过序了
// preEnd index
// [1, 3] [2, 4] [4, 7]
// 0 1 2
// maxLen(0, -1)
// 0 -> maxLen(1, 0)
// 在arr[index...]选择序列之前选的离index最近的序列位置在preIndex
// 请返回index...能链接起来的,序列数量的最大值
public static int maxLen(int[][] arr, int index, int preIndex) {
if (index == arr.length) {
return 0;
}
// 还有序列可以选
// index号序列
// 不选
int p1 = maxLen(arr, index + 1, preIndex);
// 选
int p2 = 0;
// [3,17] index(9,24)
if (arr[preIndex][1] == arr[index][0]) { // 才能选
p2 = 1 + maxLen(arr, index + 1, index);
}
return Math.max(p1, p2);
}
// O(N^2)
public static int maxNumberSubsequence(int[][] arr, int index, int pre) {
if (index == arr.length) {
return 0;
}
// 就是不要当前序列
int p1 = maxNumberSubsequence(arr, index + 1, pre);
// 要当前序列
int p2 = -1;
if (pre == -1 || arr[pre][1] == arr[index][0]) {
p2 = 1 + maxNumberSubsequence(arr, index + 1, index);
}
return Math.max(p1, p2);
}
// O(N)
}