From 64d7b5cd1fc52dc0fc33bda5ed9dcdbb8cbc5ba0 Mon Sep 17 00:00:00 2001 From: algorithmzuo Date: Wed, 27 Jul 2022 13:43:29 +0800 Subject: [PATCH] modify code --- .../Code01_MinSwapStep.java | 74 ++++++ .../Code02_MissingNumber.java | 29 +++ .../Code03_FindMinKth.java | 175 +++++++++++++ .../Code04_CoverMax.java | 84 +++++++ .../Code05_KokoEatingBananas.java | 35 +++ .../Code01_FindKMajority.java | 143 +++++++++++ .../Code02_EvenTimesOddTimes.java | 81 ++++++ .../Code03_PrintMatrixZigZag.java | 59 +++++ .../Code04_ZigZagPrintMatrix.java | 43 ++++ .../Code05_FindTheCelebrity.java | 37 +++ .../Code01_MinSwapTimes.java | 88 +++++++ .../Code02_4KeysKeyboard.java | 34 +++ .../Code03_BestMeetingPoint.java | 49 ++++ .../Code04_LowestLexicography.java | 116 +++++++++ .../Code06_SetAll.java | 48 ++++ .../Code07_InsertDeleteGetRandom.java | 65 +++++ .../Code08_MaxGap.java | 52 ++++ .../Code01_CircleCandy.java | 58 +++++ ...ngestSumSubArrayLengthInPositiveArray.java | 91 +++++++ .../Code03_LongestSumSubArrayLength.java | 143 +++++++++++ .../Code04_Ratio01Split.java | 67 +++++ .../Code05_LRUCache.java | 143 +++++++++++ .../Code06_AllTimesMinToMax.java | 67 +++++ .../Code07_FriendCircles.java | 78 ++++++ .../Code01_AppleMinBags.java | 45 ++++ .../Code02_MSumToN.java | 47 ++++ .../Code03_EatGrass.java | 96 +++++++ .../Code04_BestArrange.java | 111 ++++++++ .../Code05_CourseScheduleIII.java | 35 +++ .../Code06_MinContinuousFragment.java | 162 ++++++++++++ .../Code07_PoemProblem.java | 141 +++++++++++ .../题目汇总和解法.txt | 51 ++++ .../Code01_CordCoverMaxPoint.java | 87 +++++++ ...ngestSumSubArrayLengthInPositiveArray.java | 91 +++++++ .../Code03_Heaters.java | 113 +++++++++ .../Code04_TrappingRainWater.java | 28 +++ .../Code05_BSNearLeft.java | 75 ++++++ .../Code06_IsStepSum.java | 58 +++++ .../Code07_MinWindowLength.java | 114 +++++++++ .../Code08_SplitArrayLargestSum.java | 49 ++++ .../Code09_MonotonousStack.java | 165 ++++++++++++ .../Code10_LargestRectangleInHistogram.java | 58 +++++ .../第01期_mca_08_dp/Code01_Knapsack.java | 158 ++++++++++++ .../第01期_mca_08_dp/Code02_SumWays.java | 27 ++ ...stSubstringWithoutRepeatingCharacters.java | 27 ++ .../Code04_SubArrayMaxSum.java | 35 +++ .../Code05_CardsInLine.java | 208 +++++++++++++++ .../Code06_BurstBalloons.java | 180 +++++++++++++ .../Code07_LongestCommonSubsequence.java | 67 +++++ .../第01期_mca_08_dp/Code08_EditCost.java | 89 +++++++ .../Code09_SplitApples.java | 153 +++++++++++ .../第01期_mca_08_dp/Code10_HorseJump.java | 109 ++++++++ .../Code01_BestTimeToBuyAndSellStock.java | 21 ++ .../Code01_PalindromeSubsequence.java | 121 +++++++++ .../Code01_SumNoPositiveMinCost.java | 197 +++++++++++++++ .../Code02_AllLessNumSubArray.java | 109 ++++++++ .../Code02_BestTimeToBuyAndSellStockII.java | 17 ++ .../Code02_CoinsWayEveryPaperDifferent.java | 77 ++++++ .../Code02_ContainerWithMostWater.java | 32 +++ .../Code02_ConvertToLetterString.java | 123 +++++++++ .../第01期_test/Code02_HorseJump.java | 109 ++++++++ .../Code02_LessMoneySplitGold.java | 79 ++++++ .../第01期_test/Code02_MinCoinsNoLimit.java | 121 +++++++++ .../第01期_test/Code02_SubArrayMaxSum.java | 35 +++ .../Code03_BestTimeToBuyAndSellStockIII.java | 23 ++ .../第01期_test/Code03_CoinsWayNoLimit.java | 108 ++++++++ .../第01期_test/Code03_SplitNumber.java | 85 +++++++ .../第01期_test/Code03_SubMatrixMaxSum.java | 75 ++++++ .../Code03_TrappingRainWaterII.java | 76 ++++++ .../Code04_BestTimeToBuyAndSellStockIV.java | 65 +++++ .../第01期_test/Code04_DistinctSubseq.java | 100 ++++++++ .../Code04_LongestCommonSubsequence.java | 67 +++++ .../Code04_RegularExpressionMatch.java | 135 ++++++++++ .../Code04_SplitArrayLargestSum.java | 193 ++++++++++++++ ...BestTimeToBuyAndSellStockWithCooldown.java | 101 ++++++++ .../Code05_BoatsToSavePeople.java | 32 +++ .../Code05_DistinctSubseqValue.java | 50 ++++ .../Code05_LongestIncreasingPath.java | 54 ++++ .../第01期_test/Code05_MinWindowLength.java | 80 ++++++ .../第01期_test/Code05_WorldBreak.java | 237 ++++++++++++++++++ ...meToBuyAndSellStockWithTransactionFee.java | 27 ++ .../Code06_ClosestSubsequenceSum.java | 46 ++++ .../Code07_MinLengthForSort.java | 30 +++ .../第01期_test/Code07_MoneyProblem.java | 169 +++++++++++++ .../第01期_test/Code07_TargetSum.java | 127 ++++++++++ .../第01期_test/Code11_StoneGameIV.java | 63 +++++ .../Problem_0152_MaximumProductSubarray.java | 45 ++++ .../Problem_0213_HouseRobberII.java | 50 ++++ .../Problem_0265_PaintHouseII.java | 56 +++++ ...stringWithAtLeastKRepeatingCharacters.java | 110 ++++++++ .../Problem_1488_AvoidFloodInTheCity.java | 81 ++++++ 91 files changed, 7764 insertions(+) create mode 100644 MCA算法突击课/第01期_mca_01_about_sort/Code01_MinSwapStep.java create mode 100644 MCA算法突击课/第01期_mca_01_about_sort/Code02_MissingNumber.java create mode 100644 MCA算法突击课/第01期_mca_01_about_sort/Code03_FindMinKth.java create mode 100644 MCA算法突击课/第01期_mca_01_about_sort/Code04_CoverMax.java create mode 100644 MCA算法突击课/第01期_mca_01_about_sort/Code05_KokoEatingBananas.java create mode 100644 MCA算法突击课/第01期_mca_02_about_coding/Code01_FindKMajority.java create mode 100644 MCA算法突击课/第01期_mca_02_about_coding/Code02_EvenTimesOddTimes.java create mode 100644 MCA算法突击课/第01期_mca_02_about_coding/Code03_PrintMatrixZigZag.java create mode 100644 MCA算法突击课/第01期_mca_02_about_coding/Code04_ZigZagPrintMatrix.java create mode 100644 MCA算法突击课/第01期_mca_02_about_coding/Code05_FindTheCelebrity.java create mode 100644 MCA算法突击课/第01期_mca_03_about_experience/Code01_MinSwapTimes.java create mode 100644 MCA算法突击课/第01期_mca_03_about_experience/Code02_4KeysKeyboard.java create mode 100644 MCA算法突击课/第01期_mca_03_about_experience/Code03_BestMeetingPoint.java create mode 100644 MCA算法突击课/第01期_mca_03_about_experience/Code04_LowestLexicography.java create mode 100644 MCA算法突击课/第01期_mca_03_about_experience/Code06_SetAll.java create mode 100644 MCA算法突击课/第01期_mca_03_about_experience/Code07_InsertDeleteGetRandom.java create mode 100644 MCA算法突击课/第01期_mca_03_about_experience/Code08_MaxGap.java create mode 100644 MCA算法突击课/第01期_mca_04_about_data_structure/Code01_CircleCandy.java create mode 100644 MCA算法突击课/第01期_mca_04_about_data_structure/Code02_LongestSumSubArrayLengthInPositiveArray.java create mode 100644 MCA算法突击课/第01期_mca_04_about_data_structure/Code03_LongestSumSubArrayLength.java create mode 100644 MCA算法突击课/第01期_mca_04_about_data_structure/Code04_Ratio01Split.java create mode 100644 MCA算法突击课/第01期_mca_04_about_data_structure/Code05_LRUCache.java create mode 100644 MCA算法突击课/第01期_mca_04_about_data_structure/Code06_AllTimesMinToMax.java create mode 100644 MCA算法突击课/第01期_mca_04_about_data_structure/Code07_FriendCircles.java create mode 100644 MCA算法突击课/第01期_mca_05_about_math_greedy/Code01_AppleMinBags.java create mode 100644 MCA算法突击课/第01期_mca_05_about_math_greedy/Code02_MSumToN.java create mode 100644 MCA算法突击课/第01期_mca_05_about_math_greedy/Code03_EatGrass.java create mode 100644 MCA算法突击课/第01期_mca_05_about_math_greedy/Code04_BestArrange.java create mode 100644 MCA算法突击课/第01期_mca_05_about_math_greedy/Code05_CourseScheduleIII.java create mode 100644 MCA算法突击课/第01期_mca_05_about_math_greedy/Code06_MinContinuousFragment.java create mode 100644 MCA算法突击课/第01期_mca_05_about_math_greedy/Code07_PoemProblem.java create mode 100644 MCA算法突击课/第01期_mca_06_about_resources_limit/题目汇总和解法.txt create mode 100644 MCA算法突击课/第01期_mca_07_about_monotonicity/Code01_CordCoverMaxPoint.java create mode 100644 MCA算法突击课/第01期_mca_07_about_monotonicity/Code02_LongestSumSubArrayLengthInPositiveArray.java create mode 100644 MCA算法突击课/第01期_mca_07_about_monotonicity/Code03_Heaters.java create mode 100644 MCA算法突击课/第01期_mca_07_about_monotonicity/Code04_TrappingRainWater.java create mode 100644 MCA算法突击课/第01期_mca_07_about_monotonicity/Code05_BSNearLeft.java create mode 100644 MCA算法突击课/第01期_mca_07_about_monotonicity/Code06_IsStepSum.java create mode 100644 MCA算法突击课/第01期_mca_07_about_monotonicity/Code07_MinWindowLength.java create mode 100644 MCA算法突击课/第01期_mca_07_about_monotonicity/Code08_SplitArrayLargestSum.java create mode 100644 MCA算法突击课/第01期_mca_07_about_monotonicity/Code09_MonotonousStack.java create mode 100644 MCA算法突击课/第01期_mca_07_about_monotonicity/Code10_LargestRectangleInHistogram.java create mode 100644 MCA算法突击课/第01期_mca_08_dp/Code01_Knapsack.java create mode 100644 MCA算法突击课/第01期_mca_08_dp/Code02_SumWays.java create mode 100644 MCA算法突击课/第01期_mca_08_dp/Code03_LongestSubstringWithoutRepeatingCharacters.java create mode 100644 MCA算法突击课/第01期_mca_08_dp/Code04_SubArrayMaxSum.java create mode 100644 MCA算法突击课/第01期_mca_08_dp/Code05_CardsInLine.java create mode 100644 MCA算法突击课/第01期_mca_08_dp/Code06_BurstBalloons.java create mode 100644 MCA算法突击课/第01期_mca_08_dp/Code07_LongestCommonSubsequence.java create mode 100644 MCA算法突击课/第01期_mca_08_dp/Code08_EditCost.java create mode 100644 MCA算法突击课/第01期_mca_08_dp/Code09_SplitApples.java create mode 100644 MCA算法突击课/第01期_mca_08_dp/Code10_HorseJump.java create mode 100644 MCA算法突击课/第01期_test/Code01_BestTimeToBuyAndSellStock.java create mode 100644 MCA算法突击课/第01期_test/Code01_PalindromeSubsequence.java create mode 100644 MCA算法突击课/第01期_test/Code01_SumNoPositiveMinCost.java create mode 100644 MCA算法突击课/第01期_test/Code02_AllLessNumSubArray.java create mode 100644 MCA算法突击课/第01期_test/Code02_BestTimeToBuyAndSellStockII.java create mode 100644 MCA算法突击课/第01期_test/Code02_CoinsWayEveryPaperDifferent.java create mode 100644 MCA算法突击课/第01期_test/Code02_ContainerWithMostWater.java create mode 100644 MCA算法突击课/第01期_test/Code02_ConvertToLetterString.java create mode 100644 MCA算法突击课/第01期_test/Code02_HorseJump.java create mode 100644 MCA算法突击课/第01期_test/Code02_LessMoneySplitGold.java create mode 100644 MCA算法突击课/第01期_test/Code02_MinCoinsNoLimit.java create mode 100644 MCA算法突击课/第01期_test/Code02_SubArrayMaxSum.java create mode 100644 MCA算法突击课/第01期_test/Code03_BestTimeToBuyAndSellStockIII.java create mode 100644 MCA算法突击课/第01期_test/Code03_CoinsWayNoLimit.java create mode 100644 MCA算法突击课/第01期_test/Code03_SplitNumber.java create mode 100644 MCA算法突击课/第01期_test/Code03_SubMatrixMaxSum.java create mode 100644 MCA算法突击课/第01期_test/Code03_TrappingRainWaterII.java create mode 100644 MCA算法突击课/第01期_test/Code04_BestTimeToBuyAndSellStockIV.java create mode 100644 MCA算法突击课/第01期_test/Code04_DistinctSubseq.java create mode 100644 MCA算法突击课/第01期_test/Code04_LongestCommonSubsequence.java create mode 100644 MCA算法突击课/第01期_test/Code04_RegularExpressionMatch.java create mode 100644 MCA算法突击课/第01期_test/Code04_SplitArrayLargestSum.java create mode 100644 MCA算法突击课/第01期_test/Code05_BestTimeToBuyAndSellStockWithCooldown.java create mode 100644 MCA算法突击课/第01期_test/Code05_BoatsToSavePeople.java create mode 100644 MCA算法突击课/第01期_test/Code05_DistinctSubseqValue.java create mode 100644 MCA算法突击课/第01期_test/Code05_LongestIncreasingPath.java create mode 100644 MCA算法突击课/第01期_test/Code05_MinWindowLength.java create mode 100644 MCA算法突击课/第01期_test/Code05_WorldBreak.java create mode 100644 MCA算法突击课/第01期_test/Code06_BestTimeToBuyAndSellStockWithTransactionFee.java create mode 100644 MCA算法突击课/第01期_test/Code06_ClosestSubsequenceSum.java create mode 100644 MCA算法突击课/第01期_test/Code07_MinLengthForSort.java create mode 100644 MCA算法突击课/第01期_test/Code07_MoneyProblem.java create mode 100644 MCA算法突击课/第01期_test/Code07_TargetSum.java create mode 100644 MCA算法突击课/第01期_test/Code11_StoneGameIV.java create mode 100644 MCA算法突击课/第01期_test/Problem_0152_MaximumProductSubarray.java create mode 100644 MCA算法突击课/第01期_test/Problem_0213_HouseRobberII.java create mode 100644 MCA算法突击课/第01期_test/Problem_0265_PaintHouseII.java create mode 100644 MCA算法突击课/第01期_test/Problem_0395_LongestSubstringWithAtLeastKRepeatingCharacters.java create mode 100644 MCA算法突击课/第01期_test/Problem_1488_AvoidFloodInTheCity.java diff --git a/MCA算法突击课/第01期_mca_01_about_sort/Code01_MinSwapStep.java b/MCA算法突击课/第01期_mca_01_about_sort/Code01_MinSwapStep.java new file mode 100644 index 0000000..252e2d8 --- /dev/null +++ b/MCA算法突击课/第01期_mca_01_about_sort/Code01_MinSwapStep.java @@ -0,0 +1,74 @@ +package 第01期_mca_01_about_sort; + +public class Code01_MinSwapStep { + + // 一个数组中只有两种字符'G'和'B', + // 可以让所有的G都放在左侧,所有的B都放在右侧 + // 或者可以让所有的G都放在右侧,所有的B都放在左侧 + // 但是只能在相邻字符之间进行交换操作,请问请问至少需要交换几次, + public static int minSteps1(String s) { + if (s == null || s.equals("")) { + return 0; + } + char[] str = s.toCharArray(); + int step1 = 0; + int gi = 0; + for (int i = 0; i < str.length; i++) { + if (str[i] == 'G') { + step1 += i - (gi++); + } + } + int step2 = 0; + int bi = 0; + for (int i = 0; i < str.length; i++) { + if (str[i] == 'B') { + step2 += i - (bi++); + } + } + return Math.min(step1, step2); + } + + // 可以让G在左,或者在右 + public static int minSteps2(String s) { + if (s == null || s.equals("")) { + return 0; + } + char[] str = s.toCharArray(); + int step1 = 0; + int step2 = 0; + int gi = 0; + int bi = 0; + for (int i = 0; i < str.length; i++) { + if (str[i] == 'G') { // 当前的G,去左边 方案1 + step1 += i - (gi++); + } else {// 当前的B,去左边 方案2 + step2 += i - (bi++); + } + } + return Math.min(step1, step2); + } + + // 为了测试 + public static String randomString(int maxLen) { + char[] str = new char[(int) (Math.random() * maxLen)]; + for (int i = 0; i < str.length; i++) { + str[i] = Math.random() < 0.5 ? 'G' : 'B'; + } + return String.valueOf(str); + } + + public static void main(String[] args) { + int maxLen = 100; + int testTime = 1000000; + System.out.println("测试开始"); + for (int i = 0; i < testTime; i++) { + String str = randomString(maxLen); + int ans1 = minSteps1(str); + int ans2 = minSteps2(str); + if (ans1 != ans2) { + System.out.println("Oops!"); + } + } + System.out.println("测试结束"); + } +} \ No newline at end of file diff --git a/MCA算法突击课/第01期_mca_01_about_sort/Code02_MissingNumber.java b/MCA算法突击课/第01期_mca_01_about_sort/Code02_MissingNumber.java new file mode 100644 index 0000000..3c48a7a --- /dev/null +++ b/MCA算法突击课/第01期_mca_01_about_sort/Code02_MissingNumber.java @@ -0,0 +1,29 @@ +package 第01期_mca_01_about_sort; + +// 测试链接:https://leetcode.com/problems/first-missing-positive/ +public class Code02_MissingNumber { + + public static int firstMissingPositive(int[] arr) { + // l是盯着的位置 + // 0 ~ L-1有效区 + int L = 0; + int R = arr.length; + while (L != R) { + if (arr[L] == L + 1) { + L++; + } else if (arr[L] <= L || arr[L] > R || arr[arr[L] - 1] == arr[L]) { // 垃圾的情况 + swap(arr, L, --R); + } else { + swap(arr, L, arr[L] - 1); + } + } + return L + 1; + } + + public static void swap(int[] arr, int i, int j) { + int tmp = arr[i]; + arr[i] = arr[j]; + arr[j] = tmp; + } + +} diff --git a/MCA算法突击课/第01期_mca_01_about_sort/Code03_FindMinKth.java b/MCA算法突击课/第01期_mca_01_about_sort/Code03_FindMinKth.java new file mode 100644 index 0000000..3463091 --- /dev/null +++ b/MCA算法突击课/第01期_mca_01_about_sort/Code03_FindMinKth.java @@ -0,0 +1,175 @@ +package 第01期_mca_01_about_sort; + +import java.util.Comparator; +import java.util.PriorityQueue; + +public class Code03_FindMinKth { + + public static class MaxHeapComparator implements Comparator { + + @Override + public int compare(Integer o1, Integer o2) { + return o2 - o1; + } + + } + + // 利用大根堆,时间复杂度O(N*logK) + public static int minKth1(int[] arr, int k) { + PriorityQueue maxHeap = new PriorityQueue<>(new MaxHeapComparator()); + for (int i = 0; i < k; i++) { + maxHeap.add(arr[i]); + } + for (int i = k; i < arr.length; i++) { + if (arr[i] < maxHeap.peek()) { + maxHeap.poll(); + maxHeap.add(arr[i]); + } + } + return maxHeap.peek(); + } + + // 改写快排,时间复杂度O(N) + // k >= 1 + public static int minKth2(int[] array, int k) { + int[] arr = copyArray(array); + return process2(arr, 0, arr.length - 1, k - 1); + } + + public static int[] copyArray(int[] arr) { + int[] ans = new int[arr.length]; + for (int i = 0; i != ans.length; i++) { + ans[i] = arr[i]; + } + return ans; + } + + // arr 第k小的数 + // process2(arr, 0, N-1, k-1) + // arr[L..R] 范围上,如果排序的话(不是真的去排序),找位于index的数 + // index [L..R] + public static int process2(int[] arr, int L, int R, int index) { + if (L == R) { // L = =R ==INDEX + return arr[L]; + } + // 不止一个数 L + [0, R -L] + int pivot = arr[L + (int) (Math.random() * (R - L + 1))]; + int[] range = partition(arr, L, R, pivot); + if (index >= range[0] && index <= range[1]) { + return arr[index]; + } else if (index < range[0]) { + return process2(arr, L, range[0] - 1, index); + } else { + return process2(arr, range[1] + 1, R, index); + } + } + + public static int[] partition(int[] arr, int L, int R, int pivot) { + int less = L - 1; + int more = R + 1; + int cur = L; + while (cur < more) { + if (arr[cur] < pivot) { + swap(arr, ++less, cur++); + } else if (arr[cur] > pivot) { + swap(arr, cur, --more); + } else { + cur++; + } + } + return new int[] { less + 1, more - 1 }; + } + + public static void swap(int[] arr, int i1, int i2) { + int tmp = arr[i1]; + arr[i1] = arr[i2]; + arr[i2] = tmp; + } + + // 利用bfprt算法,时间复杂度O(N) + public static int minKth3(int[] array, int k) { + int[] arr = copyArray(array); + return bfprt(arr, 0, arr.length - 1, k - 1); + } + + // arr[L..R] 如果排序的话,位于index位置的数,是什么,返回 + public static int bfprt(int[] arr, int L, int R, int index) { + if (L == R) { + return arr[L]; + } + // L...R 每五个数一组 + // 每一个小组内部排好序 + // 小组的中位数组成新数组 + // 这个新数组的中位数返回 + int pivot = medianOfMedians(arr, L, R); + int[] range = partition(arr, L, R, pivot); + if (index >= range[0] && index <= range[1]) { + return arr[index]; + } else if (index < range[0]) { + return bfprt(arr, L, range[0] - 1, index); + } else { + return bfprt(arr, range[1] + 1, R, index); + } + } + + // arr[L...R] 五个数一组 + // 每个小组内部排序 + // 每个小组中位数领出来,组成marr + // marr中的中位数,返回 + public static int medianOfMedians(int[] arr, int L, int R) { + int size = R - L + 1; + int offset = size % 5 == 0 ? 0 : 1; + int[] mArr = new int[size / 5 + offset]; + for (int team = 0; team < mArr.length; team++) { + int teamFirst = L + team * 5; + // L ... L + 4 + // L +5 ... L +9 + // L +10....L+14 + mArr[team] = getMedian(arr, teamFirst, Math.min(R, teamFirst + 4)); + } + // marr中,找到中位数 + // marr(0, marr.len - 1, mArr.length / 2 ) + return bfprt(mArr, 0, mArr.length - 1, mArr.length / 2); + } + + public static int getMedian(int[] arr, int L, int R) { + insertionSort(arr, L, R); + return arr[(L + R) / 2]; + } + + public static void insertionSort(int[] arr, int L, int R) { + for (int i = L + 1; i <= R; i++) { + for (int j = i - 1; j >= L && arr[j] > arr[j + 1]; j--) { + swap(arr, j, j + 1); + } + } + } + + // for test + public static int[] generateRandomArray(int maxSize, int maxValue) { + int[] arr = new int[(int) (Math.random() * maxSize) + 1]; + for (int i = 0; i < arr.length; i++) { + arr[i] = (int) (Math.random() * (maxValue + 1)); + } + return arr; + } + + public static void main(String[] args) { + int testTime = 1000000; + int maxSize = 100; + int maxValue = 100; + System.out.println("test begin"); + for (int i = 0; i < testTime; i++) { + int[] arr = generateRandomArray(maxSize, maxValue); + int k = (int) (Math.random() * arr.length) + 1; + int ans1 = minKth1(arr, k); + int ans2 = minKth2(arr, k); + int ans3 = minKth3(arr, k); + if (ans1 != ans2 || ans2 != ans3) { + System.out.println("Oops!"); + } + } + System.out.println("test finish"); + } + +} diff --git a/MCA算法突击课/第01期_mca_01_about_sort/Code04_CoverMax.java b/MCA算法突击课/第01期_mca_01_about_sort/Code04_CoverMax.java new file mode 100644 index 0000000..53f8139 --- /dev/null +++ b/MCA算法突击课/第01期_mca_01_about_sort/Code04_CoverMax.java @@ -0,0 +1,84 @@ +package 第01期_mca_01_about_sort; + +import java.util.Arrays; +import java.util.PriorityQueue; + +public class Code04_CoverMax { + + // [3,7] + // [0,10] + // [1,17] + public static int zuo(int[][] lines) { + // 根据开始位置排序 + Arrays.sort(lines, (a, b) -> a[0] - b[0]); + PriorityQueue heap = new PriorityQueue<>(); + // 堆:加入一个数字,弹出一个数字,时间复杂度O(log N) + int max = 0; + for (int[] line : lines) { + int start = line[0]; + int end = line[1]; + while (!heap.isEmpty() && heap.peek() <= start) { + heap.poll(); + } + heap.add(end); + int curAns = heap.size(); + max = Math.max(max, curAns); + + } + return max; + } + + public static int maxCover1(int[][] lines) { + int min = Integer.MAX_VALUE; + int max = Integer.MIN_VALUE; + for (int i = 0; i < lines.length; i++) { + min = Math.min(min, lines[i][0]); + max = Math.max(max, lines[i][1]); + } + int cover = 0; + for (double p = min + 0.5; p < max; p += 1) { + int cur = 0; + for (int i = 0; i < lines.length; i++) { + if (lines[i][0] < p && lines[i][1] > p) { + cur++; + } + } + cover = Math.max(cover, cur); + } + return cover; + } + + // for test + public static int[][] generateLines(int N, int L, int R) { + int size = (int) (Math.random() * N) + 1; + int[][] ans = new int[size][2]; + for (int i = 0; i < size; i++) { + int a = L + (int) (Math.random() * (R - L + 1)); + int b = L + (int) (Math.random() * (R - L + 1)); + if (a == b) { + b = a + 1; + } + ans[i][0] = Math.min(a, b); + ans[i][1] = Math.max(a, b); + } + return ans; + } + + public static void main(String[] args) { + System.out.println("test begin"); + int N = 100; + int L = 0; + int R = 200; + int testTimes = 200000; + for (int i = 0; i < testTimes; i++) { + int[][] lines = generateLines(N, L, R); + int ans1 = maxCover1(lines); + int ans2 = zuo(lines); + if (ans1 != ans2) { + System.out.println("Oops!"); + } + } + System.out.println("test end"); + } + +} diff --git a/MCA算法突击课/第01期_mca_01_about_sort/Code05_KokoEatingBananas.java b/MCA算法突击课/第01期_mca_01_about_sort/Code05_KokoEatingBananas.java new file mode 100644 index 0000000..fd5dbf0 --- /dev/null +++ b/MCA算法突击课/第01期_mca_01_about_sort/Code05_KokoEatingBananas.java @@ -0,0 +1,35 @@ +package 第01期_mca_01_about_sort; + +// leetcode 875题 +public class Code05_KokoEatingBananas { + + public static int minEatingSpeed(int[] piles, int h) { + int L = 1; + int R = 0; + for (int pile : piles) { + R = Math.max(R, pile); + } + int ans = 0; + int M = 0; + while (L <= R) { + M = L + ((R - L) >> 1); + if (hours(piles, M) <= h) { + ans = M; + R = M - 1; + } else { + L = M + 1; + } + } + return ans; + } + + public static int hours(int[] piles, int speed) { + int ans = 0; + int offset = speed - 1; + for (int pile : piles) { + ans += (pile + offset) / speed; + } + return ans; + } + +} diff --git a/MCA算法突击课/第01期_mca_02_about_coding/Code01_FindKMajority.java b/MCA算法突击课/第01期_mca_02_about_coding/Code01_FindKMajority.java new file mode 100644 index 0000000..9b4b8b2 --- /dev/null +++ b/MCA算法突击课/第01期_mca_02_about_coding/Code01_FindKMajority.java @@ -0,0 +1,143 @@ +package 第01期_mca_02_about_coding; + +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map.Entry; + +public class Code01_FindKMajority { + + public static void water(int[] arr) { + if (arr == null || arr.length == 0) { + System.out.println("没有水王数"); + } else { + int target = 0; + int hp = 0; + for (int cur : arr) { + if (hp == 0) { + target = cur; + hp = 1; + } else if (cur != target) { + hp--; + } else { + hp++; + } + } + if (hp == 0) { + System.out.println("没有水王数"); + return; + } + int times = 0; + for (int cur : arr) { + if (target == cur) { + times++; + } + } + if (times > arr.length / 2) { + System.out.println("水王数是 : " + target); + } else { + System.out.println("没有水王数"); + } + } + } + + public static void printHalfMajor(int[] arr) { + int cand = 0; + int HP = 0; + for (int i = 0; i < arr.length; i++) { + if (HP == 0) { + cand = arr[i]; + HP = 1; + } else if (arr[i] == cand) { + HP++; + } else { + HP--; + } + } + if (HP == 0) { + System.out.println("no such number."); + return; + } + HP = 0; + for (int i = 0; i < arr.length; i++) { + if (arr[i] == cand) { + HP++; + } + } + if (HP > arr.length / 2) { + System.out.println(cand); + } else { + System.out.println("no such number."); + } + } + + public static void printKMajor(int[] arr, int K) { + if (K < 2) { + System.out.println("the value of K is invalid."); + return; + } + // 攒候选,cands,候选表,最多K-1条记录! > N / K次的数字,最多有K-1个 + HashMap cands = new HashMap(); + for (int i = 0; i != arr.length; i++) { + if (cands.containsKey(arr[i])) { + cands.put(arr[i], cands.get(arr[i]) + 1); + } else { // arr[i] 不是候选 + if (cands.size() == K - 1) { // 当前数肯定不要!,每一个候选付出1点血量,血量变成0的候选,要删掉! + allCandsMinusOne(cands); + } else { + cands.put(arr[i], 1); + } + } + } + // 所有可能的候选,都在cands表中!遍历一遍arr,每个候选收集真实次数 + + HashMap reals = getReals(arr, cands); + boolean hasPrint = false; + for (Entry set : cands.entrySet()) { + Integer key = set.getKey(); + if (reals.get(key) > arr.length / K) { + hasPrint = true; + System.out.print(key + " "); + } + } + System.out.println(hasPrint ? "" : "no such number."); + } + + public static void allCandsMinusOne(HashMap map) { + List removeList = new LinkedList(); + for (Entry set : map.entrySet()) { + Integer key = set.getKey(); + Integer value = set.getValue(); + if (value == 1) { + removeList.add(key); + } + map.put(key, value - 1); + } + for (Integer removeKey : removeList) { + map.remove(removeKey); + } + } + + public static HashMap getReals(int[] arr, HashMap cands) { + HashMap reals = new HashMap(); + for (int i = 0; i != arr.length; i++) { + int curNum = arr[i]; + if (cands.containsKey(curNum)) { + if (reals.containsKey(curNum)) { + reals.put(curNum, reals.get(curNum) + 1); + } else { + reals.put(curNum, 1); + } + } + } + return reals; + } + + public static void main(String[] args) { + int[] arr = { 1, 2, 3, 1, 1, 2, 1 }; + printHalfMajor(arr); + int K = 4; + printKMajor(arr, K); + } + +} diff --git a/MCA算法突击课/第01期_mca_02_about_coding/Code02_EvenTimesOddTimes.java b/MCA算法突击课/第01期_mca_02_about_coding/Code02_EvenTimesOddTimes.java new file mode 100644 index 0000000..95f594f --- /dev/null +++ b/MCA算法突击课/第01期_mca_02_about_coding/Code02_EvenTimesOddTimes.java @@ -0,0 +1,81 @@ +package 第01期_mca_02_about_coding; + +public class Code02_EvenTimesOddTimes { + + // arr中,只有一种数,出现奇数次 + public static void printOddTimesNum1(int[] arr) { + int eor = 0; + for (int i = 0; i < arr.length; i++) { + eor ^= arr[i]; + } + System.out.println(eor); + } + + // arr中,有两种数,出现奇数次 + public static void printOddTimesNum2(int[] arr) { + int eor = 0; + for (int i = 0; i < arr.length; i++) { + eor ^= arr[i]; + } + // a 和 b是两种数 + // eor != 0 + // eor最右侧的1,提取出来 + // eor : 00110010110111000 + // rightOne :00000000000001000 + int rightOne = eor & (-eor); // 提取出最右的1 + int onlyOne = 0; // eor' + for (int i = 0 ; i < arr.length;i++) { + // arr[1] = 111100011110000 + // rightOne= 000000000010000 + if ((arr[i] & rightOne) != 0) { + onlyOne ^= arr[i]; + } + } + System.out.println(onlyOne + " " + (eor ^ onlyOne)); + } + + + public static int bit1counts(int N) { + int count = 0; + + // 011011010000 + // 000000010000 1 + + // 011011000000 + // + + + + while(N != 0) { + int rightOne = N & ((~N) + 1); + count++; + N ^= rightOne; + // N -= rightOne + } + + + return count; + + } + + + public static void main(String[] args) { + int a = 5; + int b = 7; + + a = a ^ b; + b = a ^ b; + a = a ^ b; + + System.out.println(a); + System.out.println(b); + + int[] arr1 = { 3, 3, 2, 3, 1, 1, 1, 3, 1, 1, 1 }; + printOddTimesNum1(arr1); + + int[] arr2 = { 4, 3, 4, 2, 2, 2, 4, 1, 1, 1, 3, 3, 1, 1, 1, 4, 2, 2 }; + printOddTimesNum2(arr2); + + } + +} diff --git a/MCA算法突击课/第01期_mca_02_about_coding/Code03_PrintMatrixZigZag.java b/MCA算法突击课/第01期_mca_02_about_coding/Code03_PrintMatrixZigZag.java new file mode 100644 index 0000000..f75942b --- /dev/null +++ b/MCA算法突击课/第01期_mca_02_about_coding/Code03_PrintMatrixZigZag.java @@ -0,0 +1,59 @@ +package 第01期_mca_02_about_coding; + +public class Code03_PrintMatrixZigZag { + + public static void print(int[][] matrix) { + if (matrix == null || matrix.length == 0 || matrix[0].length == 0) { + return; + } + int leftUpRow = 0; + int leftUpCol = 0; + int rightDownRow = matrix.length - 1; + int rightDownCol = matrix[0].length - 1; + // 还没结束! + while (leftUpRow <= rightDownRow && leftUpCol <= rightDownCol) { + f(matrix, leftUpRow++, leftUpCol++, rightDownRow--, rightDownCol--); + } + } + + // 打印框! + public static void f(int[][] matrix, int leftUpRow, int leftUpCol, int rightDownRow, int rightDownCol) { + if (leftUpRow == rightDownRow && leftUpCol == rightDownCol) { + System.out.print(matrix[leftUpRow][leftUpCol] + " "); + } else { // 不是一个数! + if (leftUpRow == rightDownRow) { // 当前的框是一条横线 + for (int col = leftUpCol; col <= rightDownCol; col++) { + System.out.print(matrix[leftUpRow][col] + " "); + } + } else if (leftUpCol == rightDownCol) { // 当前的框是一条竖线 + for (int row = leftUpRow; row <= rightDownRow; row++) { + System.out.print(matrix[row][leftUpCol] + " "); + } + } else { // 正常的框 + for (int col = leftUpCol; col < rightDownCol; col++) { + System.out.print(matrix[leftUpRow][col] + " "); + } + for (int row = leftUpRow; row < rightDownRow; row++) { + System.out.print(matrix[row][rightDownCol] + " "); + } + for (int col = rightDownCol; col > leftUpCol; col--) { + System.out.print(matrix[rightDownRow][col] + " "); + } + for (int row = rightDownRow; row > leftUpRow; row--) { + System.out.print(matrix[row][leftUpCol] + " "); + } + } + } + } + + public static void main(String[] args) { + int[][] matrix = { + { 1, 2, 3}, + { 4, 5, 6}, + { 7, 8 ,9} + }; + print(matrix); + + } + +} diff --git a/MCA算法突击课/第01期_mca_02_about_coding/Code04_ZigZagPrintMatrix.java b/MCA算法突击课/第01期_mca_02_about_coding/Code04_ZigZagPrintMatrix.java new file mode 100644 index 0000000..6692002 --- /dev/null +++ b/MCA算法突击课/第01期_mca_02_about_coding/Code04_ZigZagPrintMatrix.java @@ -0,0 +1,43 @@ +package 第01期_mca_02_about_coding; + +public class Code04_ZigZagPrintMatrix { + + public static void printMatrixZigZag(int[][] matrix) { + int tR = 0; + int tC = 0; + int dR = 0; + int dC = 0; + int endR = matrix.length - 1; + int endC = matrix[0].length - 1; + boolean fromUp = false; + while (tR != endR + 1) { + printLevel(matrix, tR, tC, dR, dC, fromUp); + tR = tC == endC ? tR + 1 : tR; + tC = tC == endC ? tC : tC + 1; + dC = dR == endR ? dC + 1 : dC; + dR = dR == endR ? dR : dR + 1; + fromUp = !fromUp; + } + System.out.println(); + } + + public static void printLevel(int[][] m, + int Arow, int ACol, int Brow, int Bcol, boolean f) { + if (f) { + while (Arow != Brow + 1) { + System.out.print(m[Arow++][ACol--] + " "); + } + } else { + while (Brow != Arow - 1) { + System.out.print(m[Brow--][Bcol++] + " "); + } + } + } + + public static void main(String[] args) { + int[][] matrix = { { 1, 2, 3, 4 }, { 5, 6, 7, 8 }, { 9, 10, 11, 12 } }; + printMatrixZigZag(matrix); + + } + +} diff --git a/MCA算法突击课/第01期_mca_02_about_coding/Code05_FindTheCelebrity.java b/MCA算法突击课/第01期_mca_02_about_coding/Code05_FindTheCelebrity.java new file mode 100644 index 0000000..1e898a3 --- /dev/null +++ b/MCA算法突击课/第01期_mca_02_about_coding/Code05_FindTheCelebrity.java @@ -0,0 +1,37 @@ +package 第01期_mca_02_about_coding; + +public class Code05_FindTheCelebrity { + // 提交时不要提交这个函数,因为默认系统会给你这个函数 + // knows方法,自己不认识自己 + public static boolean knows(int x, int i) { + return true; + } + + // 只提交下面的方法 0 ~ n-1 + public int findCelebrity(int n) { + // 谁可能成为明星,谁就是cand + int cand = 0; + for (int i = 0; i < n; ++i) { + if (knows(cand, i)) { + cand = i; + } + } + // cand是什么?唯一可能是明星的人! + // 下一步就是验证,它到底是不是明星 + // 1) cand是不是不认识所有的人 cand...(右侧cand都不认识) + // 所以,只用验证 ....cand的左侧即可 + for (int i = 0; i < cand; ++i) { + if (knows(cand, i)) { + return -1; + } + } + // 2) 是不是所有的人都认识cand + for (int i = 0; i < n; ++i) { + if (!knows(i, cand)) { + return -1; + } + } + return cand; + } + +} diff --git a/MCA算法突击课/第01期_mca_03_about_experience/Code01_MinSwapTimes.java b/MCA算法突击课/第01期_mca_03_about_experience/Code01_MinSwapTimes.java new file mode 100644 index 0000000..13a99a3 --- /dev/null +++ b/MCA算法突击课/第01期_mca_03_about_experience/Code01_MinSwapTimes.java @@ -0,0 +1,88 @@ +package 第01期_mca_03_about_experience; + +// 来自小红书 +// 一个无序数组长度为n,所有数字都不一样,并且值都在[0...n-1]范围上 +// 返回让这个无序数组变成有序数组的最小交换次数 +public class Code01_MinSwapTimes { + + // 纯暴力,arr长度大一点都会超时 + // 但是绝对正确 + public static int minSwap1(int[] arr) { + return process1(arr, 0); + } + + // 让arr变有序,最少的交换次数是多少!返回 + // times, 之前已经做了多少次交换 + public static int process1(int[] arr, int times) { + boolean sorted = true; + for (int i = 1; i < arr.length; i++) { + if (arr[i - 1] > arr[i]) { + sorted = false; + break; + } + } + if (sorted) { + return times; + } + // 数组现在是无序的状态! + if (times >= arr.length - 1) { + return Integer.MAX_VALUE; + } + int ans = Integer.MAX_VALUE; + for (int i = 0; i < arr.length; i++) { + for (int j = i + 1; j < arr.length; j++) { + swap(arr, i, j); + ans = Math.min(ans, process1(arr, times + 1)); + swap(arr, i, j); + } + } + return ans; + } + + public static void swap(int[] arr, int i, int j) { + int tmp = arr[i]; + arr[i] = arr[j]; + arr[j] = tmp; + } + + // 已知arr中,只有0~n-1这些值,并且都出现1次 + public static int minSwap2(int[] arr) { + int ans = 0; + for (int i = 0; i < arr.length; i++) { + while (i != arr[i]) { + swap(arr, i, arr[i]); + ans++; + } + } + return ans; + } + + // 为了测试 + public static int[] randomArray(int len) { + int[] arr = new int[len]; + for (int i = 0; i < len; i++) { + arr[i] = i; + } + for (int i = 0; i < len; i++) { + swap(arr, i, (int) (Math.random() * len)); + } + return arr; + } + + public static void main(String[] args) { + int n = 6; + int testTime = 2000; + System.out.println("测试开始"); + for (int i = 0; i < testTime; i++) { + int len = (int) (Math.random() * n) + 1; + int[] arr = randomArray(len); + int ans1 = minSwap1(arr); + int ans2 = minSwap2(arr); + if (ans1 != ans2) { + System.out.println("出错了!"); + } + } + System.out.println("测试结束"); + } + +} diff --git a/MCA算法突击课/第01期_mca_03_about_experience/Code02_4KeysKeyboard.java b/MCA算法突击课/第01期_mca_03_about_experience/Code02_4KeysKeyboard.java new file mode 100644 index 0000000..85ee768 --- /dev/null +++ b/MCA算法突击课/第01期_mca_03_about_experience/Code02_4KeysKeyboard.java @@ -0,0 +1,34 @@ +package 第01期_mca_03_about_experience; + +// 测试链接 : https://leetcode.com/problems/4-keys-keyboard/ +public class Code02_4KeysKeyboard { + + // 可以证明: + // 来到i的时候,包括i在内最多有连续4次粘贴行为 + // 不可能更多,如果有连续5次粘贴,一定就不再是最优解 + // 假设开始时,A的数量为S,看如下的变化过程,我们称这是行为一: + // 开始 全选 复制(粘贴板S个A) 粘贴 粘贴 粘贴 粘贴 粘贴 + // S S S 2*S 3*S 4*S 5*S 6*S + // 但是,注意看如下的行为二: + // 开始 全选 复制(粘贴板S个A) 粘贴 全选 复制(粘贴板2S个A) 粘贴 粘贴 + // S S S 2*S 2*S 2*S 4*S 6*S + // 行为一,经历8步,最后是6*S个A + // 行为二,经历8步,最后是6*S个A + // 但是行为二在粘贴板上有2S个A,而行为一在粘贴板上有S个A + // 所以行为一没有行为二优 + // 以此说明:来到i的时候,包括i在内最多有连续4次粘贴行为 + // 那么就尝试:连续1次、连续2次、连续3次、连续4次粘贴行为即可 + public static int maxA(int n) { + int[] dp = new int[n + 1]; + for (int i = 1; i <= 6 && i <= n; i++) { + dp[i] = i; + } + for (int i = 7; i <= n; i++) { + dp[i] = Math.max( + Math.max(dp[i - 3] * 2, dp[i - 4] * 3), + Math.max(dp[i - 5] * 4, dp[i - 6] * 5)); + } + return dp[n]; + } + +} diff --git a/MCA算法突击课/第01期_mca_03_about_experience/Code03_BestMeetingPoint.java b/MCA算法突击课/第01期_mca_03_about_experience/Code03_BestMeetingPoint.java new file mode 100644 index 0000000..0861e7c --- /dev/null +++ b/MCA算法突击课/第01期_mca_03_about_experience/Code03_BestMeetingPoint.java @@ -0,0 +1,49 @@ +package 第01期_mca_03_about_experience; + +// 测试链接 : https://leetcode.com/problems/best-meeting-point/ +public class Code03_BestMeetingPoint { + + public static int minTotalDistance(int[][] grid) { + int N = grid.length; + int M = grid[0].length; + int[] iOnes = new int[N]; + int[] jOnes = new int[M]; + for (int i = 0; i < N; i++) { + for (int j = 0; j < M; j++) { + if (grid[i][j] == 1) { + iOnes[i]++; + jOnes[j]++; + } + } + } + int total = 0; + int i = 0; + int j = N - 1; + int iRest = 0; + int jRest = 0; + while (i < j) { + if (iOnes[i] + iRest <= iOnes[j] + jRest) { + total += iOnes[i] + iRest; + iRest += iOnes[i++]; + } else { + total += iOnes[j] + jRest; + jRest += iOnes[j--]; + } + } + i = 0; + j = M - 1; + iRest = 0; + jRest = 0; + while (i < j) { + if (jOnes[i] + iRest <= jOnes[j] + jRest) { + total += jOnes[i] + iRest; + iRest += jOnes[i++]; + } else { + total += jOnes[j] + jRest; + jRest += jOnes[j--]; + } + } + return total; + } + +} diff --git a/MCA算法突击课/第01期_mca_03_about_experience/Code04_LowestLexicography.java b/MCA算法突击课/第01期_mca_03_about_experience/Code04_LowestLexicography.java new file mode 100644 index 0000000..be55569 --- /dev/null +++ b/MCA算法突击课/第01期_mca_03_about_experience/Code04_LowestLexicography.java @@ -0,0 +1,116 @@ +package 第01期_mca_03_about_experience; + +import java.util.Arrays; +import java.util.Comparator; +import java.util.TreeSet; + +public class Code04_LowestLexicography { + + public static String lowestString1(String[] strs) { + if (strs == null || strs.length == 0) { + return ""; + } + TreeSet ans = process(strs); + return ans.size() == 0 ? "" : ans.first(); + } + + // strs中所有字符串全排列,返回所有可能的结果 + public static TreeSet process(String[] strs) { + TreeSet ans = new TreeSet<>(); + if (strs.length == 0) { + ans.add(""); + return ans; + } + for (int i = 0; i < strs.length; i++) { + String first = strs[i]; + String[] nexts = removeIndexString(strs, i); + TreeSet next = process(nexts); + for (String cur : next) { + ans.add(first + cur); + } + } + return ans; + } + + // {"abc", "cks", "bct"} + // 0 1 2 + // removeIndexString(arr , 1) -> {"abc", "bct"} + public static String[] removeIndexString(String[] arr, int index) { + int N = arr.length; + String[] ans = new String[N - 1]; + int ansIndex = 0; + for (int i = 0; i < N; i++) { + if (i != index) { + ans[ansIndex++] = arr[i]; + } + } + return ans; + } + + public static class MyComparator implements Comparator { + @Override + public int compare(String a, String b) { + return (a + b).compareTo(b + a); + } + } + + public static String lowestString2(String[] strs) { + if (strs == null || strs.length == 0) { + return ""; + } + Arrays.sort(strs, new MyComparator()); + String res = ""; + for (int i = 0; i < strs.length; i++) { + res += strs[i]; + } + return res; + } + + // for test + public static String generateRandomString(int strLen) { + char[] ans = new char[(int) (Math.random() * strLen) + 1]; + for (int i = 0; i < ans.length; i++) { + int value = (int) (Math.random() * 5); + ans[i] = (Math.random() <= 0.5) ? (char) (65 + value) : (char) (97 + value); + } + return String.valueOf(ans); + } + + // for test + public static String[] generateRandomStringArray(int arrLen, int strLen) { + String[] ans = new String[(int) (Math.random() * arrLen) + 1]; + for (int i = 0; i < ans.length; i++) { + ans[i] = generateRandomString(strLen); + } + return ans; + } + + // for test + public static String[] copyStringArray(String[] arr) { + String[] ans = new String[arr.length]; + for (int i = 0; i < ans.length; i++) { + ans[i] = String.valueOf(arr[i]); + } + return ans; + } + + public static void main(String[] args) { + int arrLen = 6; + int strLen = 5; + int testTimes = 10000; + System.out.println("test begin"); + for (int i = 0; i < testTimes; i++) { + String[] arr1 = generateRandomStringArray(arrLen, strLen); + String[] arr2 = copyStringArray(arr1); + if (!lowestString1(arr1).equals(lowestString2(arr2))) { + for (String str : arr1) { + System.out.print(str + ","); + } + System.out.println(); + System.out.println("Oops!"); + } + } + System.out.println("finish!"); + } + +} diff --git a/MCA算法突击课/第01期_mca_03_about_experience/Code06_SetAll.java b/MCA算法突击课/第01期_mca_03_about_experience/Code06_SetAll.java new file mode 100644 index 0000000..6c821df --- /dev/null +++ b/MCA算法突击课/第01期_mca_03_about_experience/Code06_SetAll.java @@ -0,0 +1,48 @@ +package 第01期_mca_03_about_experience; + +import java.util.HashMap; + +public class Code06_SetAll { + + public static class MyValue { + public V value; + public long time; + + public MyValue(V v, long t) { + value = v; + time = t; + } + } + + public static class MyHashMap { + private HashMap> map; + private long time; + private MyValue setAll; + + public MyHashMap() { + map = new HashMap<>(); + time = 0; + setAll = new MyValue(null, -1); + } + + public void put(K key, V value) { + map.put(key, new MyValue(value, time++)); + } + + public void setAll(V value) { + setAll = new MyValue(value, time++); + } + + public V get(K key) { + if (!map.containsKey(key)) { + return null; + } + if (map.get(key).time > setAll.time) { + return map.get(key).value; + } else { + return setAll.value; + } + } + } + +} diff --git a/MCA算法突击课/第01期_mca_03_about_experience/Code07_InsertDeleteGetRandom.java b/MCA算法突击课/第01期_mca_03_about_experience/Code07_InsertDeleteGetRandom.java new file mode 100644 index 0000000..ac59af8 --- /dev/null +++ b/MCA算法突击课/第01期_mca_03_about_experience/Code07_InsertDeleteGetRandom.java @@ -0,0 +1,65 @@ +package 第01期_mca_03_about_experience; + +import java.util.HashMap; + +public class Code07_InsertDeleteGetRandom { + + public class RandomizedSet { + + // 600 -> 0 + private HashMap keyIndexMap; + // 0 -> 600 + private HashMap indexKeyMap; + private int size; + + public RandomizedSet() { + keyIndexMap = new HashMap(); + indexKeyMap = new HashMap(); + size = 0; + } + + public boolean insert(int key) { + if (!keyIndexMap.containsKey(key)) { + keyIndexMap.put(key, size); + indexKeyMap.put(size++, key); + return true; + } + return false; + } + + public boolean remove(int val) { + + // c -> 2 + + // remove c + // 最后的位置:9 - 1 + // deleteIndex = 2; + // lastIndex = 8 + // 8 -> z + // z -> 2 + // 2 -> c(z) + // c -> X + // 8 -> z X + if (keyIndexMap.containsKey(val)) { + int deleteIndex = keyIndexMap.get(val); + int lastIndex = --size; + int lastKey = indexKeyMap.get(lastIndex); + keyIndexMap.put(lastKey, deleteIndex); + indexKeyMap.put(deleteIndex, lastKey); + keyIndexMap.remove(val); + indexKeyMap.remove(lastIndex); + return true; + } + return false; + } + + public int getRandom() { + if (size == 0) { + return -1; + } + int randomIndex = (int) (Math.random() * size); + return indexKeyMap.get(randomIndex); + } + } + +} diff --git a/MCA算法突击课/第01期_mca_03_about_experience/Code08_MaxGap.java b/MCA算法突击课/第01期_mca_03_about_experience/Code08_MaxGap.java new file mode 100644 index 0000000..8b6edb9 --- /dev/null +++ b/MCA算法突击课/第01期_mca_03_about_experience/Code08_MaxGap.java @@ -0,0 +1,52 @@ +package 第01期_mca_03_about_experience; + +// 测试链接 : https://leetcode.com/problems/maximum-gap/ +public class Code08_MaxGap { + + public static int maximumGap(int[] nums) { + if (nums == null || nums.length < 2) { + return 0; + } + int N = nums.length; + int min = Integer.MAX_VALUE; + int max = Integer.MIN_VALUE; + for (int i = 0; i < N; i++) { + min = Math.min(min, nums[i]); + max = Math.max(max, nums[i]); + } + if (min == max) { + return 0; + } + // N 个数 N + 1个桶 + // 0~9 min max boolean + // 10~19 min max boolean + boolean[] hasNum = new boolean[N + 1]; + int[] maxs = new int[N + 1]; + int[] mins = new int[N + 1]; + int bid = 0; + for (int i = 0; i < N; i++) { + // 17 0~99 10个桶,算一下,我该去哪个桶 + bid = bucket(nums[i], N, min, max); + mins[bid] = hasNum[bid] ? Math.min(mins[bid], nums[i]) : nums[i]; + maxs[bid] = hasNum[bid] ? Math.max(maxs[bid], nums[i]) : nums[i]; + hasNum[bid] = true; + } + int res = 0; + int lastMax = maxs[0]; + int i = 1; + // 当前不空的桶,min - 之前不空的桶max, + // 把差值最大的记录! + for (; i <= N; i++) { + if (hasNum[i]) { + res = Math.max(res, mins[i] - lastMax); + lastMax = maxs[i]; + } + } + return res; + } + + public static int bucket(long num, long len, long min, long max) { + return (int) ((num - min) * len / (max - min)); + } + +} diff --git a/MCA算法突击课/第01期_mca_04_about_data_structure/Code01_CircleCandy.java b/MCA算法突击课/第01期_mca_04_about_data_structure/Code01_CircleCandy.java new file mode 100644 index 0000000..5e56dc5 --- /dev/null +++ b/MCA算法突击课/第01期_mca_04_about_data_structure/Code01_CircleCandy.java @@ -0,0 +1,58 @@ +package 第01期_mca_04_about_data_structure; + +// 来自网易 +// 给定一个正数数组arr,表示每个小朋友的得分 +// 任何两个相邻的小朋友,如果得分一样,怎么分糖果无所谓,但如果得分不一样,分数大的一定要比分数少的多拿一些糖果 +// 假设所有的小朋友坐成一个环形,返回在不破坏上一条规则的情况下,需要的最少糖果数 +public class Code01_CircleCandy { + + public static int minCandy(int[] arr) { + if (arr == null || arr.length == 0) { + return 0; + } + if (arr.length == 1) { + return 1; + } + int n = arr.length; + int minIndex = 0; + for (int i = 0; i < n; i++) { + if (arr[i] <= arr[lastIndex(i, n)] && arr[i] <= arr[nextIndex(i, n)]) { + minIndex = i; + break; + } + } + int[] nums = new int[n + 1]; + for (int i = 0; i <= n; i++, minIndex = nextIndex(minIndex, n)) { + nums[i] = arr[minIndex]; + } + int[] left = new int[n + 1]; + left[0] = 1; + for (int i = 1; i <= n; i++) { + left[i] = nums[i] > nums[i - 1] ? (left[i - 1] + 1) : 1; + } + int[] right = new int[n + 1]; + right[n] = 1; + for (int i = n - 1; i >= 0; i--) { + right[i] = nums[i] > nums[i + 1] ? (right[i + 1] + 1) : 1; + } + int ans = 0; + for (int i = 0; i < n; i++) { + ans += Math.max(left[i], right[i]); + } + return ans; + } + + public static int nextIndex(int i, int n) { + return i == n - 1 ? 0 : (i + 1); + } + + public static int lastIndex(int i, int n) { + return i == 0 ? (n - 1) : (i - 1); + } + + public static void main(String[] args) { + int[] arr = { 3, 4, 2, 3, 2 }; + System.out.println(minCandy(arr)); + } + +} diff --git a/MCA算法突击课/第01期_mca_04_about_data_structure/Code02_LongestSumSubArrayLengthInPositiveArray.java b/MCA算法突击课/第01期_mca_04_about_data_structure/Code02_LongestSumSubArrayLengthInPositiveArray.java new file mode 100644 index 0000000..16e37e4 --- /dev/null +++ b/MCA算法突击课/第01期_mca_04_about_data_structure/Code02_LongestSumSubArrayLengthInPositiveArray.java @@ -0,0 +1,91 @@ +package 第01期_mca_04_about_data_structure; + +public class Code02_LongestSumSubArrayLengthInPositiveArray { + + public static int getMaxLength(int[] arr, int K) { + if (arr == null || arr.length == 0 || K <= 0) { + return 0; + } + int left = 0; + int right = 0; + int sum = arr[0]; + int len = 0; + while (right < arr.length) { + if (sum == K) { + len = Math.max(len, right - left + 1); + sum -= arr[left++]; + } else if (sum < K) { + right++; + if (right == arr.length) { + break; + } + sum += arr[right]; + } else { + sum -= arr[left++]; + } + } + return len; + } + + // for test + public static int right(int[] arr, int K) { + int max = 0; + for (int i = 0; i < arr.length; i++) { + for (int j = i; j < arr.length; j++) { + if (valid(arr, i, j, K)) { + max = Math.max(max, j - i + 1); + } + } + } + return max; + } + + // for test + public static boolean valid(int[] arr, int L, int R, int K) { + int sum = 0; + for (int i = L; i <= R; i++) { + sum += arr[i]; + } + return sum == K; + } + + // for test + public static int[] generatePositiveArray(int size, int value) { + int[] ans = new int[size]; + for (int i = 0; i != size; i++) { + ans[i] = (int) (Math.random() * value) + 1; + } + return ans; + } + + // for test + public static void printArray(int[] arr) { + for (int i = 0; i != arr.length; i++) { + System.out.print(arr[i] + " "); + } + System.out.println(); + } + + public static void main(String[] args) { + int len = 50; + int value = 100; + int testTime = 500000; + System.out.println("test begin"); + for (int i = 0; i < testTime; i++) { + int[] arr = generatePositiveArray(len, value); + int K = (int) (Math.random() * value) + 1; + int ans1 = getMaxLength(arr, K); + int ans2 = right(arr, K); + if (ans1 != ans2) { + System.out.println("Oops!"); + printArray(arr); + System.out.println("K : " + K); + System.out.println(ans1); + System.out.println(ans2); + break; + } + } + System.out.println("test end"); + } + +} diff --git a/MCA算法突击课/第01期_mca_04_about_data_structure/Code03_LongestSumSubArrayLength.java b/MCA算法突击课/第01期_mca_04_about_data_structure/Code03_LongestSumSubArrayLength.java new file mode 100644 index 0000000..ad5fdfa --- /dev/null +++ b/MCA算法突击课/第01期_mca_04_about_data_structure/Code03_LongestSumSubArrayLength.java @@ -0,0 +1,143 @@ +package 第01期_mca_04_about_data_structure; + +import java.util.HashMap; + +// +public class Code03_LongestSumSubArrayLength { + + + + + public static int sumTargetMaxLength(int[] arr, int target) { + if(arr == null || arr.length == 0) { + return 0; + } + // 0~4 100 + // (100, 4) + // 0~8 100 + // (100, 4) + // key :某个前缀和 + // value : 最早出现的位置! + HashMap preSumEarlyMap = new HashMap<>(); + // 一个数也没有的时候,有前缀和!是0! + preSumEarlyMap.put(0, -1); + int sum = 0; // 0 ~ i 整体前缀和! + int ans = 0; + for(int i = 0; i < arr.length;i++) { + sum += arr[i]; + int findPreSum = sum - target; + if(preSumEarlyMap.containsKey(findPreSum)) { + int earlyIndex = preSumEarlyMap.get(findPreSum); + int iendAns = i - earlyIndex; + ans = Math.max(ans, iendAns); + } + if(!preSumEarlyMap.containsKey(sum)) { + preSumEarlyMap.put(sum, i); + } + } + return ans; + } + + + + + + + + + + + + + + + + + + + public static int maxLength(int[] arr, int k) { + if (arr == null || arr.length == 0) { + return 0; + } + // key:前缀和 + // value : 0~value这个前缀和是最早出现key这个值的 + HashMap map = new HashMap(); + map.put(0, -1); // important + int len = 0; + int sum = 0; + for (int i = 0; i < arr.length; i++) { + sum += arr[i]; + if (map.containsKey(sum - k)) { + len = Math.max(i - map.get(sum - k), len); + } + if (!map.containsKey(sum)) { + map.put(sum, i); + } + } + return len; + } + + // for test + public static int right(int[] arr, int K) { + int max = 0; + for (int i = 0; i < arr.length; i++) { + for (int j = i; j < arr.length; j++) { + if (valid(arr, i, j, K)) { + max = Math.max(max, j - i + 1); + } + } + } + return max; + } + + // for test + public static boolean valid(int[] arr, int L, int R, int K) { + int sum = 0; + for (int i = L; i <= R; i++) { + sum += arr[i]; + } + return sum == K; + } + + // for test + public static int[] generateRandomArray(int size, int value) { + int[] ans = new int[(int) (Math.random() * size) + 1]; + for (int i = 0; i < ans.length; i++) { + ans[i] = (int) (Math.random() * value) - (int) (Math.random() * value); + } + return ans; + } + + // for test + public static void printArray(int[] arr) { + for (int i = 0; i != arr.length; i++) { + System.out.print(arr[i] + " "); + } + System.out.println(); + } + + public static void main(String[] args) { + int len = 50; + int value = 100; + int testTime = 500000; + + System.out.println("test begin"); + for (int i = 0; i < testTime; i++) { + int[] arr = generateRandomArray(len, value); + int K = (int) (Math.random() * value) - (int) (Math.random() * value); + int ans1 = maxLength(arr, K); + int ans2 = right(arr, K); + if (ans1 != ans2) { + System.out.println("Oops!"); + printArray(arr); + System.out.println("K : " + K); + System.out.println(ans1); + System.out.println(ans2); + break; + } + } + System.out.println("test end"); + + } + +} diff --git a/MCA算法突击课/第01期_mca_04_about_data_structure/Code04_Ratio01Split.java b/MCA算法突击课/第01期_mca_04_about_data_structure/Code04_Ratio01Split.java new file mode 100644 index 0000000..47c8fab --- /dev/null +++ b/MCA算法突击课/第01期_mca_04_about_data_structure/Code04_Ratio01Split.java @@ -0,0 +1,67 @@ +package 第01期_mca_04_about_data_structure; + +import java.util.HashMap; + +// 来自京东 +// 把一个01字符串切成多个部分,要求每一部分的0和1比例一样,同时要求尽可能多的划分 +// 比如 : 01010101 +// 01 01 01 01 这是一种切法,0和1比例为 1 : 1 +// 0101 0101 也是一种切法,0和1比例为 1 : 1 +// 两种切法都符合要求,但是那么尽可能多的划分为第一种切法,部分数为4 +// 比如 : 00001111 +// 只有一种切法就是00001111整体作为一块,那么尽可能多的划分,部分数为1 +// 给定一个01字符串str,假设长度为N,要求返回一个长度为N的数组ans +// 其中ans[i] = str[0...i]这个前缀串,要求每一部分的0和1比例一样,同时要求尽可能多的划分下,部分数是多少 +// 输入: str = "010100001" +// 输出: ans = [1, 1, 1, 2, 1, 2, 1, 1, 3] +public class Code04_Ratio01Split { + + // 001010010100... + public static int[] split(int[] arr) { + // key : 分子 + // value : 属于key的分母表, 每一个分母,及其 分子/分母 这个比例,多少个前缀拥有 + HashMap> pre = new HashMap<>(); + int n = arr.length; + int[] ans = new int[n]; + int zero = 0; // 0出现的次数 + int one = 0; // 1出现的次数 + for (int i = 0; i < n; i++) { + if (arr[i] == 0) { + zero++; + } else { + one++; + } + if (zero == 0 || one == 0) { + ans[i] = i + 1; + } else { // 0和1,都有数量 -> 最简分数 + int gcd = gcd(zero, one); + int a = zero / gcd; + int b = one / gcd; + // a / b 比例,之前有多少前缀拥有? 3+1 4 5+1 6 + if (!pre.containsKey(a)) { + pre.put(a, new HashMap<>()); + } + if (!pre.get(a).containsKey(b)) { + pre.get(a).put(b, 1); + } else { + pre.get(a).put(b, pre.get(a).get(b) + 1); + } + ans[i] = pre.get(a).get(b); + } + } + return ans; + } + + public static int gcd(int m, int n) { + return n == 0 ? m : gcd(n, m % n); + } + + public static void main(String[] args) { + int[] arr = { 0, 1, 0, 1, 0, 1, 1, 0 }; + int[] ans = split(arr); + for (int i = 0; i < ans.length; i++) { + System.out.print(ans[i] + " "); + } + } + +} diff --git a/MCA算法突击课/第01期_mca_04_about_data_structure/Code05_LRUCache.java b/MCA算法突击课/第01期_mca_04_about_data_structure/Code05_LRUCache.java new file mode 100644 index 0000000..f57b4d6 --- /dev/null +++ b/MCA算法突击课/第01期_mca_04_about_data_structure/Code05_LRUCache.java @@ -0,0 +1,143 @@ +package 第01期_mca_04_about_data_structure; + +import java.util.HashMap; + +// 本题测试链接 : https://leetcode.com/problems/lru-cache/ +public class Code05_LRUCache { + + public class LRUCache { + + public LRUCache(int capacity) { + cache = new MyCache(capacity); + } + + private MyCache cache; + + public int get(int key) { + return cache.get(key); + } + + public void put(int key, int value) { + cache.set(key, value); + } + + public class Node { + public int key; + public int value; + public Node last; + public Node next; + + public Node(int k, int v) { + key = k; + value = v; + } + } + + // 自己要实现的双向链表! + public class NodeDoubleLinkedList { + private Node head; + private Node tail; + + public NodeDoubleLinkedList() { + head = null; + tail = null; + } + + // 让你加一个节点!往尾巴上加! + public void addNode(Node newNode) { + if (newNode == null) { + return; + } + if (head == null) { + head = newNode; + tail = newNode; + } else { + tail.next = newNode; + newNode.last = tail; + tail = newNode; + } + } + + // 一定能保证,x就在双向链表上! + // 请把,x之前,和x之后,的节点之间重连!抠出x + // 把x放到尾巴上去! + public void moveNodeToTail(Node x) { + if (tail == x) { + return; + } + // x不是尾巴!x右边一定是有节点的!不为空! + if (head == x) { // 前边没有节点,x直接去尾巴!,head要往下走! + head = x.next; + head.last = null; + } else { // x有前!也有后! + x.last.next = x.next; + x.next.last = x.last; + } + x.last = tail; + x.next = null; + tail.next = x; + tail = x; + } + + public Node removeHead() { + if (head == null) { + return null; + } + Node res = head; + if (head == tail) { + head = null; + tail = null; + } else { + head = res.next; + res.next = null; + head.last = null; + } + return res; + } + + } + + public class MyCache { + private HashMap keyNodeMap; + private NodeDoubleLinkedList nodeList; + private final int capacity; + + public MyCache(int cap) { + keyNodeMap = new HashMap<>(); + nodeList = new NodeDoubleLinkedList(); + capacity = cap; + } + + public int get(int key) { + if (keyNodeMap.containsKey(key)) { + Node res = keyNodeMap.get(key); + nodeList.moveNodeToTail(res); + return res.value; + } + return -1; + } + + public void set(int key, int value) { + if (keyNodeMap.containsKey(key)) { + Node node = keyNodeMap.get(key); + node.value = value; + nodeList.moveNodeToTail(node); + } else { + Node newNode = new Node(key, value); + keyNodeMap.put(key, newNode); + nodeList.addNode(newNode); + if (keyNodeMap.size() == capacity + 1) { + removeMostUnusedCache(); + } + } + } + + private void removeMostUnusedCache() { + Node removeNode = nodeList.removeHead(); + keyNodeMap.remove(removeNode.key); + } + + } + } + +} diff --git a/MCA算法突击课/第01期_mca_04_about_data_structure/Code06_AllTimesMinToMax.java b/MCA算法突击课/第01期_mca_04_about_data_structure/Code06_AllTimesMinToMax.java new file mode 100644 index 0000000..019ca47 --- /dev/null +++ b/MCA算法突击课/第01期_mca_04_about_data_structure/Code06_AllTimesMinToMax.java @@ -0,0 +1,67 @@ +package 第01期_mca_04_about_data_structure; + +import java.util.Stack; + +public class Code06_AllTimesMinToMax { + + public static int max1(int[] arr) { + int max = Integer.MIN_VALUE; + for (int i = 0; i < arr.length; i++) { + for (int j = i; j < arr.length; j++) { + int minNum = Integer.MAX_VALUE; + int sum = 0; + for (int k = i; k <= j; k++) { + sum += arr[k]; + minNum = Math.min(minNum, arr[k]); + } + max = Math.max(max, minNum * sum); + } + } + return max; + } + + public static int max2(int[] arr) { + int size = arr.length; + int[] sums = new int[size]; + sums[0] = arr[0]; + for (int i = 1; i < size; i++) { + sums[i] = sums[i - 1] + arr[i]; + } + int max = Integer.MIN_VALUE; + Stack stack = new Stack(); + for (int i = 0; i < size; i++) { + while (!stack.isEmpty() && arr[stack.peek()] >= arr[i]) { + int j = stack.pop(); + max = Math.max(max, (stack.isEmpty() ? sums[i - 1] : (sums[i - 1] - sums[stack.peek()])) * arr[j]); + } + stack.push(i); + } + while (!stack.isEmpty()) { + int j = stack.pop(); + max = Math.max(max, (stack.isEmpty() ? sums[size - 1] : (sums[size - 1] - sums[stack.peek()])) * arr[j]); + } + return max; + } + + public static int[] gerenareRondomArray() { + int[] arr = new int[(int) (Math.random() * 20) + 10]; + for (int i = 0; i < arr.length; i++) { + arr[i] = (int) (Math.random() * 101); + } + return arr; + } + + public static void main(String[] args) { + int testTimes = 2000000; + System.out.println("test begin"); + for (int i = 0; i < testTimes; i++) { + int[] arr = gerenareRondomArray(); + if (max1(arr) != max2(arr)) { + System.out.println("FUCK!"); + break; + } + } + System.out.println("test finish"); + } + +} diff --git a/MCA算法突击课/第01期_mca_04_about_data_structure/Code07_FriendCircles.java b/MCA算法突击课/第01期_mca_04_about_data_structure/Code07_FriendCircles.java new file mode 100644 index 0000000..fbc53c1 --- /dev/null +++ b/MCA算法突击课/第01期_mca_04_about_data_structure/Code07_FriendCircles.java @@ -0,0 +1,78 @@ +package 第01期_mca_04_about_data_structure; + +// 本题为leetcode原题 +// 测试链接:https://leetcode.com/problems/friend-circles/ +// 可以直接通过 +public class Code07_FriendCircles { + + public static int findCircleNum(int[][] M) { + int N = M.length; + // {0} {1} {2} {N-1} + UnionFind unionFind = new UnionFind(N); + for (int i = 0; i < N; i++) { + for (int j = i + 1; j < N; j++) { + if (M[i][j] == 1) { // i和j互相认识 + unionFind.union(i, j); + } + } + } + return unionFind.sets(); + } + + public static class UnionFind { + // parent[i] = k : i的父亲是k + private int[] parent; + // size[i] = k : 如果i是代表节点,size[i]才有意义,否则无意义 + // i所在的集合大小是多少 + private int[] size; + // 辅助结构 + private int[] help; + // 一共有多少个集合 + private int sets; + + public UnionFind(int N) { + parent = new int[N]; + size = new int[N]; + help = new int[N]; + sets = N; + for (int i = 0; i < N; i++) { + parent[i] = i; + size[i] = 1; + } + } + + // 从i开始一直往上,往上到不能再往上,代表节点,返回 + // 这个过程要做路径压缩 + private int find(int i) { + int hi = 0; + while (i != parent[i]) { + help[hi++] = i; + i = parent[i]; + } + for (hi--; hi >= 0; hi--) { + parent[help[hi]] = i; + } + return i; + } + + public void union(int i, int j) { + int f1 = find(i); + int f2 = find(j); + if (f1 != f2) { + if (size[f1] >= size[f2]) { + size[f1] += size[f2]; + parent[f2] = f1; + } else { + size[f2] += size[f1]; + parent[f1] = f2; + } + sets--; + } + } + + public int sets() { + return sets; + } + } + +} diff --git a/MCA算法突击课/第01期_mca_05_about_math_greedy/Code01_AppleMinBags.java b/MCA算法突击课/第01期_mca_05_about_math_greedy/Code01_AppleMinBags.java new file mode 100644 index 0000000..9e15ebc --- /dev/null +++ b/MCA算法突击课/第01期_mca_05_about_math_greedy/Code01_AppleMinBags.java @@ -0,0 +1,45 @@ +package 第01期_mca_05_about_math_greedy; + +public class Code01_AppleMinBags { + + public static int minBags(int apple) { + if (apple < 0) { + return -1; + } + // apple >> 3 -> apple / 8 + // 最多用几个8号袋! + int bag8 = (apple >> 3); + // 最多的8号袋,先试试,剩余多少苹果 + int rest = apple - (bag8 << 3); + while(bag8 >= 0) { + // rest 个 + if(rest % 6 ==0) { + return bag8 + (rest / 6); + } else { + bag8--; + rest += 8; + } + } + return -1; + } + + // O(1) + public static int minBagAwesome(int apple) { + if ((apple & 1) != 0) { // 如果是奇数,返回-1 + return -1; + } + if (apple < 18) { + return apple == 0 ? 0 : (apple == 6 || apple == 8) ? 1 + : (apple == 12 || apple == 14 || apple == 16) ? 2 : -1; + } + return (apple - 18) / 8 + 3; + } + + public static void main(String[] args) { + for(int apple = 1; apple < 200;apple++) { + System.out.println(apple + " : "+ minBags(apple)); + } + + } + +} diff --git a/MCA算法突击课/第01期_mca_05_about_math_greedy/Code02_MSumToN.java b/MCA算法突击课/第01期_mca_05_about_math_greedy/Code02_MSumToN.java new file mode 100644 index 0000000..3931cc7 --- /dev/null +++ b/MCA算法突击课/第01期_mca_05_about_math_greedy/Code02_MSumToN.java @@ -0,0 +1,47 @@ +package 第01期_mca_05_about_math_greedy; + +public class Code02_MSumToN { + + public static boolean isMSum1(int num) { + for (int start = 1; start <= num; start++) { + int sum = start; + for (int j = start + 1; j <= num; j++) { + if (sum + j > num) { + break; + } + if (sum + j == num) { + return true; + } + sum += j; + } + } + return false; + } + + public static boolean isMSum2(int num) { +// +// return num == (num & (~num + 1)); // 是2的某次方,不是连续和! +// +// +// return !(num == (num & (~num + 1))); +// +// return num == (num & (-num)); +// +// + return (num & (num - 1)) != 0; + } + + public static void main(String[] args) { + for (int num = 1; num < 200; num++) { + System.out.println(num + " : " + isMSum1(num)); + } + System.out.println("test begin"); + for (int num = 1; num < 5000; num++) { + if (isMSum1(num) != isMSum2(num)) { + System.out.println("Oops!"); + } + } + System.out.println("test end"); + + } +} diff --git a/MCA算法突击课/第01期_mca_05_about_math_greedy/Code03_EatGrass.java b/MCA算法突击课/第01期_mca_05_about_math_greedy/Code03_EatGrass.java new file mode 100644 index 0000000..6fef566 --- /dev/null +++ b/MCA算法突击课/第01期_mca_05_about_math_greedy/Code03_EatGrass.java @@ -0,0 +1,96 @@ +package 第01期_mca_05_about_math_greedy; + +public class Code03_EatGrass { + + + + + + // 剩余的草,是rest + // 对于process(rest)这个过程来说,当前的先手先拿! + // 返回值,是个字符串,"先" "后" + // "先" :当前的先手赢! + // "后" : 当前的先手输! + public static String process(int rest) { + if(rest < 5) { + return (rest == 0 || rest == 2) ? "后" : "先"; + } + // rest >= 5; + // 当前的先手,可以开始选择了 + // 1 4 16 + for(int choice = 1; choice <= rest; choice *= 4) { + int next = rest - choice; + if(process(next).equals("后")) { + return "先"; + } + } + return "后"; + } + + + + + + + + + + + + + + + + // 如果n份草,最终先手赢,返回"先手" + // 如果n份草,最终后手赢,返回"后手" + public static String whoWin(int n) { + if (n < 5) { + return n == 0 || n == 2 ? "后手" : "先手"; + } + // 进到这个过程里来,当前的先手,先选 + int want = 1; + while (want <= n) { + if (whoWin(n - want).equals("后手")) { + return "先手"; + } + if (want <= (n / 4)) { + want *= 4; + } else { + break; + } + } + return "后手"; + } + + public static String winner1(int n) { + if (n < 5) { + return (n == 0 || n == 2) ? "后手" : "先手"; + } + int base = 1; + while (base <= n) { + if (winner1(n - base).equals("后手")) { + return "先手"; + } + if (base > n / 4) { // 防止base*4之后溢出 + break; + } + base *= 4; + } + return "后手"; + } + + public static String winner2(int n) { + if (n % 5 == 0 || n % 5 == 2) { + return "后手"; + } else { + return "先手"; + } + } + + public static void main(String[] args) { + for (int i = 0; i <= 50; i++) { + System.out.println(i + " : " + process(i)); + } + } + +} diff --git a/MCA算法突击课/第01期_mca_05_about_math_greedy/Code04_BestArrange.java b/MCA算法突击课/第01期_mca_05_about_math_greedy/Code04_BestArrange.java new file mode 100644 index 0000000..339bb4d --- /dev/null +++ b/MCA算法突击课/第01期_mca_05_about_math_greedy/Code04_BestArrange.java @@ -0,0 +1,111 @@ +package 第01期_mca_05_about_math_greedy; + +import java.util.Arrays; +import java.util.Comparator; + +public class Code04_BestArrange { + + public static class Program { + public int start; + public int end; + + public Program(int start, int end) { + this.start = start; + this.end = end; + } + } + + // 暴力!所有情况都尝试! + public static int bestArrange1(Program[] programs) { + if (programs == null || programs.length == 0) { + return 0; + } + return process(programs, 0, 0); + } + + // 还剩下的会议都放在programs里 + // done之前已经安排了多少会议的数量 + // timeLine目前来到的时间点是什么 + + // 目前来到timeLine的时间点,已经安排了done多的会议,剩下的会议programs可以自由安排 + // 返回能安排的最多会议数量 + public static int process(Program[] programs, int done, int timeLine) { + if (programs.length == 0) { + return done; + } + // 还剩下会议 + int max = done; + // 当前安排的会议是什么会,每一个都枚举 + for (int i = 0; i < programs.length; i++) { + if (programs[i].start >= timeLine) { + Program[] next = copyButExcept(programs, i); + max = Math.max(max, process(next, done + 1, programs[i].end)); + } + } + return max; + } + + public static Program[] copyButExcept(Program[] programs, int i) { + Program[] ans = new Program[programs.length - 1]; + int index = 0; + for (int k = 0; k < programs.length; k++) { + if (k != i) { + ans[index++] = programs[k]; + } + } + return ans; + } + + // 会议的开始时间和结束时间,都是数值,不会 < 0 + public static int bestArrange2(Program[] programs) { + Arrays.sort(programs, new ProgramComparator()); + int timeLine = 0; + int result = 0; + // 依次遍历每一个会议,结束时间早的会议先遍历 + for (int i = 0; i < programs.length; i++) { + if (timeLine <= programs[i].start) { + result++; + timeLine = programs[i].end; + } + } + return result; + } + + public static class ProgramComparator implements Comparator { + + @Override + public int compare(Program o1, Program o2) { + return o1.end - o2.end; + } + + } + + // for test + public static Program[] generatePrograms(int programSize, int timeMax) { + Program[] ans = new Program[(int) (Math.random() * (programSize + 1))]; + for (int i = 0; i < ans.length; i++) { + int r1 = (int) (Math.random() * (timeMax + 1)); + int r2 = (int) (Math.random() * (timeMax + 1)); + if (r1 == r2) { + ans[i] = new Program(r1, r1 + 1); + } else { + ans[i] = new Program(Math.min(r1, r2), Math.max(r1, r2)); + } + } + return ans; + } + + public static void main(String[] args) { + int programSize = 12; + int timeMax = 20; + int timeTimes = 1000000; + for (int i = 0; i < timeTimes; i++) { + Program[] programs = generatePrograms(programSize, timeMax); + if (bestArrange1(programs) != bestArrange2(programs)) { + System.out.println("Oops!"); + } + } + System.out.println("finish!"); + } + +} diff --git a/MCA算法突击课/第01期_mca_05_about_math_greedy/Code05_CourseScheduleIII.java b/MCA算法突击课/第01期_mca_05_about_math_greedy/Code05_CourseScheduleIII.java new file mode 100644 index 0000000..c9c2ef2 --- /dev/null +++ b/MCA算法突击课/第01期_mca_05_about_math_greedy/Code05_CourseScheduleIII.java @@ -0,0 +1,35 @@ +package 第01期_mca_05_about_math_greedy; + +import java.util.Arrays; +import java.util.PriorityQueue; + +// leetcode 630题 +public class Code05_CourseScheduleIII { + + public static int scheduleCourse(int[][] courses) { + // courses[i] = {花费,截止} + Arrays.sort(courses, (a, b) -> a[1] - b[1]); + // 花费时间的大根堆 + PriorityQueue heap = new PriorityQueue<>((a, b) -> b - a); + // 时间点 + int time = 0; + for (int[] c : courses) { + // + if (time + c[0] <= c[1]) { // 当前时间 + 花费 <= 截止时间的 + heap.add(c[0]); + time += c[0]; + } else { // 当前时间 + 花费 > 截止时间的, 只有淘汰掉某课,当前的课才能进来! + // + if (!heap.isEmpty() && heap.peek() > c[0]) { +// time -= heap.poll(); +// heap.add(c[0]); +// time += c[0]; + heap.add(c[0]); + time += c[0] - heap.poll(); + } + } + } + return heap.size(); + } + +} diff --git a/MCA算法突击课/第01期_mca_05_about_math_greedy/Code06_MinContinuousFragment.java b/MCA算法突击课/第01期_mca_05_about_math_greedy/Code06_MinContinuousFragment.java new file mode 100644 index 0000000..2e60833 --- /dev/null +++ b/MCA算法突击课/第01期_mca_05_about_math_greedy/Code06_MinContinuousFragment.java @@ -0,0 +1,162 @@ +package 第01期_mca_05_about_math_greedy; + +// 来自CMU入学申请考试 +// 给定一个长度为 N 的字符串 S,由字符'a'和'b'组成,空隙由 '?' 表示 +// 你的任务是用a字符或b字符替换每个间隙, +// 替换完成后想让连续出现同一种字符的最长子串尽可能短 +// 例如,S = "aa??bbb", +// 如果将"??"替换为"aa" ,即"aaaabbb",则由相等字符组成的最长子串长度为4 +// 如果将"??"替换为"ba" ,即"aababbb",则由相等字符组成的最长子串长度为3 +// 那么方案二是更好的结果,返回3 +// S的长度 <= 10^6 +public class Code06_MinContinuousFragment { + + // 暴力方法 + // 为了验证 + public static int minContinuous1(String s) { + if (s == null || s.length() == 0) { + return 0; + } + char[] str = s.toCharArray(); + return process1(str, 0); + } + + public static int process1(char[] str, int index) { + if (index == str.length) { + return maxLen(str); + } else { + if (str[index] != '?') { + return process1(str, index + 1); + } else { + str[index] = 'a'; + int p1 = process1(str, index + 1); + str[index] = 'b'; + int p2 = process1(str, index + 1); + str[index] = '?'; + return Math.min(p1, p2); + } + } + } + + // 正式方法 + // 时间复杂度O(N) + public static int minContinuous2(String s) { + if (s == null || s.length() == 0) { + return 0; + } + char[] str = s.toCharArray(); + int N = str.length; + int L = 0; + int R = -1; + for (int i = 0; i < N; i++) { + if (str[i] != '?') { + set(str, L, R); + L = i + 1; + R = i; + } else { + R++; + } + } + set(str, L, R); + // 下面的for循环,是单独处理,条件5) + for (int i = 1; i < N; i++) { + if (str[i] == '?') { + // baaaa?bbbbbbbba + for (L = i - 1; L >= 0 && str[L] == str[i - 1]; L--) + ; + for (R = i + 1; R < N && str[R] == str[i + 1]; R++) + ; + L = i - L - 1; + R = R - i - 1; + if (L <= R) { + str[i] = str[i - 1]; + } else { + str[i] = str[i + 1]; + } + } + } + return maxLen(str); + } + + // L...R 都是? + // 如果这一坨问号,满足1)2)3)4)中的一种,就填好 + // 如果满足5),就不填!a?b + public static void set(char[] str, int L, int R) { + int N = str.length; + if (L > R) { + return; + } + if (L == 0 && R == N - 1) { + for (int i = 0; i < N; i++) { + str[i] = (i & 1) == 0 ? 'a' : 'b'; + } + } else if (L == 0) { + for (int i = R; i >= 0; i--) { + str[i] = str[i + 1] == 'a' ? 'b' : 'a'; + } + } else if (R == N - 1) { + for (int i = L; i < str.length; i++) { + str[i] = str[i - 1] == 'a' ? 'b' : 'a'; + } + } else { + if (str[L - 1] == str[R + 1] || L != R) { + for (; L <= R; L++, R--) { + str[L] = str[L - 1] == 'a' ? 'b' : 'a'; + str[R] = str[R + 1] == 'a' ? 'b' : 'a'; + } + } + } + } + + public static int maxLen(char[] str) { + int ans = 1; + int cur = 1; + for (int i = 1; i < str.length; i++) { + if (str[i] != str[i - 1]) { + ans = Math.max(ans, cur); + cur = 1; + } else { + cur++; + } + } + ans = Math.max(ans, cur); + return ans; + } + + public static char[] arr = { 'a', 'b', '?' }; + + public static String randomString(int len) { + int N = (int) (Math.random() * (len + 1)); + char[] str = new char[N]; + for (int i = 0; i < N; i++) { + str[i] = arr[(int) (Math.random() * 3)]; + } + return String.valueOf(str); + } + + public static void main(String[] args) { + int len = 35; + int testTime = 10000; + System.out.println("测试开始"); + for (int i = 0; i < testTime; i++) { + String s = randomString(len); + int ans1 = minContinuous1(s); + int ans2 = minContinuous2(s); + if (ans1 != ans2) { + System.out.println(s); + System.out.println(ans1); + System.out.println(ans2); + } + } + System.out.println("测试结束"); + + len = 10000000; + String s = randomString(len); + long start = System.currentTimeMillis(); + minContinuous2(s); + long end = System.currentTimeMillis(); + System.out.println("运行时间(毫秒):" + (end - start)); + + } + +} diff --git a/MCA算法突击课/第01期_mca_05_about_math_greedy/Code07_PoemProblem.java b/MCA算法突击课/第01期_mca_05_about_math_greedy/Code07_PoemProblem.java new file mode 100644 index 0000000..4deda0e --- /dev/null +++ b/MCA算法突击课/第01期_mca_05_about_math_greedy/Code07_PoemProblem.java @@ -0,0 +1,141 @@ +package 第01期_mca_05_about_math_greedy; + +import java.util.Arrays; +import java.util.HashMap; + +// 来自小红书 +// 有四种诗的韵律分别为: AABB、ABAB、ABBA、AAAA +// 比如 : 1 1 3 3就属于AABB型的韵律、6 6 6 6就属于AAAA型的韵律等等 +// 一个数组arr,当然可以生成很多的子序列,如果某个子序列一直以韵律的方式连接起来,我们称这样的子序列是有效的 +// 比如, arr = { 1, 1, 15, 1, 34, 1, 2, 67, 3, 3, 2, 4, 15, 3, 17, 4, 3, 7, 52, 7, 81, 9, 9 } +// arr的一个子序列为{1, 1, 1, 1, 2, 3, 3, 2, 4, 3, 4, 3, 7, 7, 9, 9} +// 其中1, 1, 1, 1是AAAA、2, 3, 3, 2是ABBA、4, 3, 4, 3是ABAB、7, 7, 9, 9是AABB +// 可以看到,整个子序列一直以韵律的方式连接起来,所以这个子序列是有效的 +// 给定一个数组arr, 返回最长的有效子序列长度 +// 题目限制 : arr长度 <= 4000, arr中的值<= 10^9 +// 离散化之后,arr长度 <= 4000, arr中的值<= 4000 +public class Code07_PoemProblem { + + // AABB + // ABAB + // ABBA + // AAAA + public static int maxLen1(int[] arr) { + if (arr == null || arr.length < 4) { + return 0; + } + int[] path = new int[arr.length]; + return process1(arr, 0, path, 0); + } + + public static int process1(int[] arr, int index, int[] path, int size) { + if (index == arr.length) { + if (size % 4 != 0) { + return 0; + } else { + for (int i = 0; i < size; i += 4) { + if (!valid(path, i)) { + return 0; + } + } + return size; + } + } else { + int p1 = process1(arr, index + 1, path, size); + path[size] = arr[index]; + int p2 = process1(arr, index + 1, path, size + 1); + return Math.max(p1, p2); + } + } + + public static boolean valid(int[] p, int i) { + return (p[i] == p[i + 1] && p[i + 2] == p[i + 3]) + || (p[i] == p[i + 2] && p[i + 1] == p[i + 3] && p[i] != p[i + 1]) + || (p[i] == p[i + 3] && p[i + 1] == p[i + 2] && p[i] != p[i + 1]); + } + + // 课堂有同学提出了贪心策略(这题还真是有贪心策略),是正确的 + // AABB + // ABAB + // ABBA + // AAAA + // 先看前三个规则:AABB、ABAB、ABBA + // 首先A、A、B、B的全排列为: + // AABB -> AABB + // ABAB -> ABAB + // ABBA -> ABBA + // BBAA -> 等同于AABB,因为A和B谁在前、谁在后都算是 : AABB的范式 + // BABA -> 等同于ABAB,因为A和B谁在前、谁在后都算是 : ABAB的范式 + // BAAB -> 等同于ABBA,因为A和B谁在前、谁在后都算是 : ABBA的范式 + // 也就是说,AABB、ABAB、ABBA这三个规则,可以这么用: + // 只要有两个不同的数,都出现2次,那么这一共4个数就一定符合韵律规则。 + // 所以: + // 1) 当来到arr中的一个数字num的时候, + // 如果num已经出现了2次了, 只要之前还有一个和num不同的数, + // 也出现了两次,则一定符合了某个规则, 长度直接+4,然后清空所有的统计 + // 2) 当来到arr中的一个数字num的时候, + // 如果num已经出现了4次了(规则四), 长度直接+4,然后清空所有的统计 + // 但是如果我去掉某个规则,该贪心直接报废,比如韵律规则变成: + // AABB、ABAB、AAAA + // 因为少了ABBA, 所以上面的化简不成立了, 得重新分析新规则下的贪心策略 + // 而尝试的方法就更通用(也就是maxLen3),只是减少一个分支而已 + // 这个贪心费了很多心思,值得点赞! + public static int maxLen2(int[] arr) { + // 统计某个数(key),出现的次数(value) + HashMap map = new HashMap<>(); + // tow代表目前有多少数出现了2次 + int two = 0; + // ans代表目前符合韵律链接的子序列增长到了多长 + int ans = 0; + // 当前的num出现了几次 + int numTimes = 0; + for (int num : arr) { + // 对当前的num,做次数统计 + map.put(num, map.getOrDefault(num, 0) + 1); + // 把num出现的次数拿出来 + numTimes = map.get(num); + // 如果num刚刚出现了2次, 那么目前出现了2次的数,的数量,需要增加1个 + two += numTimes == 2 ? 1 : 0; + // 下面的if代表 : + // 如果目前有2个数出现2次了,可以连接了 + // 如果目前有1个数出现4次了,可以连接了 + if (two == 2 || numTimes == 4) { + ans += 4; + map.clear(); + two = 0; + } + } + return ans; + } + + // 为了测试 + public static int[] randomArray(int len, int value) { + int[] arr = new int[len]; + for (int i = 0; i < len; i++) { + arr[i] = (int) (Math.random() * value); + } + return arr; + } + + // 为了测试 + public static void main(String[] args) { + + // 1111 2332 4343 7799 + int[] test = { 1, 1, 15, 1, 34, 1, 2, 67, 3, 3, 2, 4, 15, 3, 17, 4, 3, 7, 52, 7, 81, 9, 9 }; + System.out.println(maxLen1(test)); + System.out.println(maxLen2(test)); + System.out.println("==========="); + + int len = 16; + int value = 10; + int[] arr = randomArray(len, value); + int[] arr1 = Arrays.copyOf(arr, arr.length); + int[] arr2 = Arrays.copyOf(arr, arr.length); + System.out.println(maxLen1(arr1)); + System.out.println(maxLen2(arr2)); + + System.out.println("==========="); + + } + +} diff --git a/MCA算法突击课/第01期_mca_06_about_resources_limit/题目汇总和解法.txt b/MCA算法突击课/第01期_mca_06_about_resources_limit/题目汇总和解法.txt new file mode 100644 index 0000000..2409087 --- /dev/null +++ b/MCA算法突击课/第01期_mca_06_about_resources_limit/题目汇总和解法.txt @@ -0,0 +1,51 @@ +本节并无code,因为资源限制类题目输入需要的条件较多并且真的实现代码量巨大 +面试中这类题目出现也就和面试官聊解法,不会有代码实现的要求 + + +题目1 +32位无符号整数的范围是0~4,294,967,295, +现在有一个正好包含40亿个无符号整数的文件, +所以在整个范围中必然存在没出现过的数, +可以使用最多1GB的内存,怎么找到所有未出现过的数? +进阶: +内存限制为 3KB,但是只用找到一个没出现过的数即可 + + + +题目2 +32位无符号整数的范围是0~4294967295, +现在有40亿个无符号整数,可以使用最多1GB的内存, +找出所有出现了两次的数 + + + +题目3 +32位无符号整数的范围是0~4294967295, +现在有40亿个无符号整数, +可以使用最多3K的内存,怎么找到这40亿个整数的中位数? + + + +题目4 +32位无符号整数的范围是0~4294967295, +有一个10G大小的文件,每一行都装着这种类型的数字, +整个文件是无序的,给你5G的内存空间, +请你输出一个10G大小的文件,就是原文件所有数字排序的结果 + + + +题目5 +设计一个超级uuid系统 + + + +题目6 +给定一个非常大的List list +每一个字符串类似 : "hello,world,have,hello,world" +这一个字符串中,有2个hello,2个world,1个have +请设计一种多线程处理方案,统计list中每一个字符串, +切分出来的单词数量,并且汇总 +最终返回一个HashMap表示每个字符串出现几次 + + + diff --git a/MCA算法突击课/第01期_mca_07_about_monotonicity/Code01_CordCoverMaxPoint.java b/MCA算法突击课/第01期_mca_07_about_monotonicity/Code01_CordCoverMaxPoint.java new file mode 100644 index 0000000..e3c2aaf --- /dev/null +++ b/MCA算法突击课/第01期_mca_07_about_monotonicity/Code01_CordCoverMaxPoint.java @@ -0,0 +1,87 @@ +package 第01期_mca_07_about_monotonicity; + +import java.util.Arrays; + +public class Code01_CordCoverMaxPoint { + + public static int maxPoint1(int[] arr, int L) { + int res = 1; + for (int i = 0; i < arr.length; i++) { + int nearest = nearestIndex(arr, i, arr[i] - L); + res = Math.max(res, i - nearest + 1); + } + return res; + } + + public static int nearestIndex(int[] arr, int R, int value) { + int L = 0; + int index = R; + while (L <= R) { + int mid = L + ((R - L) >> 1); + if (arr[mid] >= value) { + index = mid; + R = mid - 1; + } else { + L = mid + 1; + } + } + return index; + } + + public static int maxPoint2(int[] arr, int L) { + int left = 0; + int right = 0; + int N = arr.length; + int max = 0; + while (left < N) { + while (right < N && arr[right] - arr[left] <= L) { + right++; + } + max = Math.max(max, right - (left++)); + } + return max; + } + + // for test + public static int test(int[] arr, int L) { + int max = 0; + for (int i = 0; i < arr.length; i++) { + int pre = i - 1; + while (pre >= 0 && arr[i] - arr[pre] <= L) { + pre--; + } + max = Math.max(max, i - pre); + } + return max; + } + + // for test + public static int[] generateArray(int len, int max) { + int[] ans = new int[(int) (Math.random() * len) + 1]; + for (int i = 0; i < ans.length; i++) { + ans[i] = (int) (Math.random() * max); + } + Arrays.sort(ans); + return ans; + } + + public static void main(String[] args) { + int len = 100; + int max = 1000; + int testTime = 100000; + System.out.println("测试开始"); + for (int i = 0; i < testTime; i++) { + int L = (int) (Math.random() * max); + int[] arr = generateArray(len, max); + int ans1 = maxPoint1(arr, L); + int ans2 = maxPoint2(arr, L); + int ans3 = test(arr, L); + if (ans1 != ans2 || ans2 != ans3) { + System.out.println("oops!"); + break; + } + } + + } + +} diff --git a/MCA算法突击课/第01期_mca_07_about_monotonicity/Code02_LongestSumSubArrayLengthInPositiveArray.java b/MCA算法突击课/第01期_mca_07_about_monotonicity/Code02_LongestSumSubArrayLengthInPositiveArray.java new file mode 100644 index 0000000..5cb76b5 --- /dev/null +++ b/MCA算法突击课/第01期_mca_07_about_monotonicity/Code02_LongestSumSubArrayLengthInPositiveArray.java @@ -0,0 +1,91 @@ +package 第01期_mca_07_about_monotonicity; + +public class Code02_LongestSumSubArrayLengthInPositiveArray { + + public static int getMaxLength(int[] arr, int K) { + if (arr == null || arr.length == 0 || K <= 0) { + return 0; + } + int left = 0; + int right = 0; + int sum = arr[0]; + int len = 0; + while (right < arr.length) { + if (sum == K) { + len = Math.max(len, right - left + 1); + sum -= arr[left++]; + } else if (sum < K) { + right++; + if (right == arr.length) { + break; + } + sum += arr[right]; + } else { + sum -= arr[left++]; + } + } + return len; + } + + // for test + public static int right(int[] arr, int K) { + int max = 0; + for (int i = 0; i < arr.length; i++) { + for (int j = i; j < arr.length; j++) { + if (valid(arr, i, j, K)) { + max = Math.max(max, j - i + 1); + } + } + } + return max; + } + + // for test + public static boolean valid(int[] arr, int L, int R, int K) { + int sum = 0; + for (int i = L; i <= R; i++) { + sum += arr[i]; + } + return sum == K; + } + + // for test + public static int[] generatePositiveArray(int size, int value) { + int[] ans = new int[size]; + for (int i = 0; i != size; i++) { + ans[i] = (int) (Math.random() * value) + 1; + } + return ans; + } + + // for test + public static void printArray(int[] arr) { + for (int i = 0; i != arr.length; i++) { + System.out.print(arr[i] + " "); + } + System.out.println(); + } + + public static void main(String[] args) { + int len = 50; + int value = 100; + int testTime = 500000; + System.out.println("test begin"); + for (int i = 0; i < testTime; i++) { + int[] arr = generatePositiveArray(len, value); + int K = (int) (Math.random() * value) + 1; + int ans1 = getMaxLength(arr, K); + int ans2 = right(arr, K); + if (ans1 != ans2) { + System.out.println("Oops!"); + printArray(arr); + System.out.println("K : " + K); + System.out.println(ans1); + System.out.println(ans2); + break; + } + } + System.out.println("test end"); + } + +} diff --git a/MCA算法突击课/第01期_mca_07_about_monotonicity/Code03_Heaters.java b/MCA算法突击课/第01期_mca_07_about_monotonicity/Code03_Heaters.java new file mode 100644 index 0000000..52bfaee --- /dev/null +++ b/MCA算法突击课/第01期_mca_07_about_monotonicity/Code03_Heaters.java @@ -0,0 +1,113 @@ +package 第01期_mca_07_about_monotonicity; + +import java.util.Arrays; + +public class Code03_Heaters { + + // 比如地点是7, 9, 14 + // 供暖点的位置: 1 3 4 5 13 15 17 + // 先看地点7 + // 由1供暖,半径是6 + // 由3供暖,半径是4 + // 由4供暖,半径是3 + // 由5供暖,半径是2 + // 由13供暖,半径是6 + // 由此可知,地点7应该由供暖点5来供暖,半径是2 + // 再看地点9 + // 供暖点不回退 + // 由5供暖,半径是4 + // 由13供暖,半径是4 + // 由15供暖,半径是6 + // 由此可知,地点9应该由供暖点13来供暖,半径是4 + // 为什么是13而不是5?因为接下来的地点都会更靠右,所以半径一样的时候,就应该选更右的供暖点 + // 再看地点14 + // 供暖点不回退 + // 由13供暖,半径是1 + // 由15供暖,半径是1 + // 由17供暖,半径是3 + // 由此可知,地点14应该由供暖点15来供暖,半径是1 + // 以此类推 + public static int findRadius(int[] houses, int[] heaters) { + Arrays.sort(houses); + Arrays.sort(heaters); + int ans = 0; + // 时间复杂度O(N) + // i是地点,j是供暖点 + for (int i = 0, j = 0; i < houses.length; i++) { + while (!best(houses, heaters, i, j)) { + j++; + } + ans = Math.max(ans, Math.abs(heaters[j] - houses[i])); + } + return ans; + } + + // 这个函数含义: + // 当前的地点houses[i]由heaters[j]来供暖是最优的吗? + // 当前的地点houses[i]由heaters[j]来供暖,产生的半径是a + // 当前的地点houses[i]由heaters[j + 1]来供暖,产生的半径是b + // 如果a < b, 说明是最优,供暖不应该跳下一个位置 + // 如果a >= b, 说明不是最优,应该跳下一个位置 + public static boolean best(int[] houses, int[] heaters, int i, int j) { + return j == heaters.length - 1 + || Math.abs(heaters[j] - houses[i]) < Math.abs(heaters[j + 1] - houses[i]); + } + + // 下面这个方法不对,你能找出原因嘛?^_^ + public static int findRadius2(int[] houses, int[] heaters) { + Arrays.sort(houses); + Arrays.sort(heaters); + int ans = 0; + // 时间复杂度O(N) + // i是地点,j是供暖点 + for (int i = 0, j = 0; i < houses.length; i++) { + while (!best2(houses, heaters, i, j)) { + j++; + } + ans = Math.max(ans, Math.abs(heaters[j] - houses[i])); + } + return ans; + } + + public static boolean best2(int[] houses, int[] heaters, int i, int j) { + return j == heaters.length - 1 || Math.abs(heaters[j] - houses[i]) <= Math.abs(heaters[j + 1] - houses[i]); + } + + // 为了测试 + public static int[] randomArray(int len, int v) { + int[] arr = new int[len]; + for (int i = 0; i < len; i++) { + arr[i] = (int) (Math.random() * v) + 1; + } + return arr; + } + + // 为了测试 + public static void main(String[] args) { + int len = 5; + int v = 10; + int testTime = 10000; + for (int i = 0; i < testTime; i++) { + int[] a = randomArray(len, v); + int[] b = randomArray(len, v); + int ans1 = findRadius(a, b); + int ans2 = findRadius2(a, b); + if (ans1 != ans2) { + System.out.println("A : "); + for (int num : a) { + System.out.print(num + " "); + } + System.out.println(); + System.out.println("B : "); + for (int num : b) { + System.out.print(num + " "); + } + System.out.println(); + System.out.println(ans1); + System.out.println(ans2); + break; + } + } + } + +} diff --git a/MCA算法突击课/第01期_mca_07_about_monotonicity/Code04_TrappingRainWater.java b/MCA算法突击课/第01期_mca_07_about_monotonicity/Code04_TrappingRainWater.java new file mode 100644 index 0000000..8ba93b9 --- /dev/null +++ b/MCA算法突击课/第01期_mca_07_about_monotonicity/Code04_TrappingRainWater.java @@ -0,0 +1,28 @@ +package 第01期_mca_07_about_monotonicity; + +// 本题测试链接 : https://leetcode.com/problems/trapping-rain-water/ +public class Code04_TrappingRainWater { + + public static int trap(int[] arr) { + if (arr == null || arr.length < 2) { + return 0; + } + int N = arr.length; + int L = 1; + int leftMax = arr[0]; + int R = N - 2; + int rightMax = arr[N - 1]; + int water = 0; + while (L <= R) { + if (leftMax <= rightMax) { + water += Math.max(0, leftMax - arr[L]); + leftMax = Math.max(leftMax, arr[L++]); + } else { + water += Math.max(0, rightMax - arr[R]); + rightMax = Math.max(rightMax, arr[R--]); + } + } + return water; + } + +} diff --git a/MCA算法突击课/第01期_mca_07_about_monotonicity/Code05_BSNearLeft.java b/MCA算法突击课/第01期_mca_07_about_monotonicity/Code05_BSNearLeft.java new file mode 100644 index 0000000..d300766 --- /dev/null +++ b/MCA算法突击课/第01期_mca_07_about_monotonicity/Code05_BSNearLeft.java @@ -0,0 +1,75 @@ +package 第01期_mca_07_about_monotonicity; + +import java.util.Arrays; + +public class Code05_BSNearLeft { + + // 在arr上,找满足>=value的最左位置 + public static int nearestIndex(int[] arr, int value) { + int L = 0; + int R = arr.length - 1; + int index = -1; // 记录最左的对号 + while (L <= R) { // 至少一个数的时候 + int mid = L + ((R - L) >> 1); + if (arr[mid] >= value) { + index = mid; + R = mid - 1; + } else { + L = mid + 1; + } + } + return index; + } + + // for test + public static int test(int[] arr, int value) { + for (int i = 0; i < arr.length; i++) { + if (arr[i] >= value) { + return i; + } + } + return -1; + } + + // for test + public static int[] generateRandomArray(int maxSize, int maxValue) { + int[] arr = new int[(int) ((maxSize + 1) * Math.random())]; + for (int i = 0; i < arr.length; i++) { + arr[i] = (int) ((maxValue + 1) * Math.random()) - (int) (maxValue * Math.random()); + } + return arr; + } + + // for test + public static void printArray(int[] arr) { + if (arr == null) { + return; + } + for (int i = 0; i < arr.length; i++) { + System.out.print(arr[i] + " "); + } + System.out.println(); + } + + public static void main(String[] args) { + int testTime = 500000; + int maxSize = 10; + int maxValue = 100; + boolean succeed = true; + for (int i = 0; i < testTime; i++) { + int[] arr = generateRandomArray(maxSize, maxValue); + Arrays.sort(arr); + int value = (int) ((maxValue + 1) * Math.random()) - (int) (maxValue * Math.random()); + if (test(arr, value) != nearestIndex(arr, value)) { + printArray(arr); + System.out.println(value); + System.out.println(test(arr, value)); + System.out.println(nearestIndex(arr, value)); + succeed = false; + break; + } + } + System.out.println(succeed ? "Nice!" : "Fucking fucked!"); + } + +} diff --git a/MCA算法突击课/第01期_mca_07_about_monotonicity/Code06_IsStepSum.java b/MCA算法突击课/第01期_mca_07_about_monotonicity/Code06_IsStepSum.java new file mode 100644 index 0000000..1add66c --- /dev/null +++ b/MCA算法突击课/第01期_mca_07_about_monotonicity/Code06_IsStepSum.java @@ -0,0 +1,58 @@ +package 第01期_mca_07_about_monotonicity; + +import java.util.HashMap; + +public class Code06_IsStepSum { + + public static boolean isStepSum(int stepSum) { + int L = 0; + int R = stepSum; + int M = 0; + int cur = 0; + while (L <= R) { + M = L + ((R - L) >> 1); + cur = stepSum(M); + if (cur == stepSum) { + return true; + } else if (cur < stepSum) { + L = M + 1; + } else { + R = M - 1; + } + } + return false; + } + + public static int stepSum(int num) { + int sum = 0; + while (num != 0) { + sum += num; + num /= 10; + } + return sum; + } + + // for test + public static HashMap generateStepSumNumberMap(int numMax) { + HashMap map = new HashMap<>(); + for (int i = 0; i <= numMax; i++) { + map.put(stepSum(i), i); + } + return map; + } + + // for test + public static void main(String[] args) { + int max = 1000000; + int maxStepSum = stepSum(max); + HashMap ans = generateStepSumNumberMap(max); + System.out.println("测试开始"); + for (int i = 0; i <= maxStepSum; i++) { + if (isStepSum(i) ^ ans.containsKey(i)) { + System.out.println("出错了!"); + } + } + System.out.println("测试结束"); + } + +} diff --git a/MCA算法突击课/第01期_mca_07_about_monotonicity/Code07_MinWindowLength.java b/MCA算法突击课/第01期_mca_07_about_monotonicity/Code07_MinWindowLength.java new file mode 100644 index 0000000..b7b3ede --- /dev/null +++ b/MCA算法突击课/第01期_mca_07_about_monotonicity/Code07_MinWindowLength.java @@ -0,0 +1,114 @@ +package 第01期_mca_07_about_monotonicity; + +public class Code07_MinWindowLength { + + // 字符串都是小写字母 + public static int validMinLen(String s1, String s2) { + char[] str1 = s1.toCharArray(); + char[] str2 = s2.toCharArray(); + int n = str1.length; + int m = str2.length; + if (n < m) { + return -1; + } + // n >= m + int[] counts = new int[256]; + for (char s2char : str2) { + counts[s2char]++; + } + int all = m; + int ans = Integer.MAX_VALUE; + int r = 0; + // [i..r) + for (int i = 0; i < n; i++) { + r = Math.max(r, i); + while (r < n && all > 0) { + counts[str1[r]]--; + if (counts[str1[r]] >= 0) { + all--; + } + r++; + } + if (all == 0) { + ans = Math.min(ans, r - i); + } + counts[str1[i]]++; + if (counts[str1[i]] > 0) { + all++; + } + } + return ans == Integer.MAX_VALUE ? -1 : ans; + } + + public static int minLength(String s1, String s2) { + if (s1 == null || s2 == null || s1.length() < s2.length()) { + return Integer.MAX_VALUE; + } + char[] str1 = s1.toCharArray(); + char[] str2 = s2.toCharArray(); + int[] map = new int[256]; // map[37] = 4 37 4次 + for (int i = 0; i != str2.length; i++) { + map[str2[i]]++; + } + int all = str2.length; + int L = 0; + int R = 0; + int minLen = Integer.MAX_VALUE; + while (R != str1.length) { + map[str1[R]]--; + if (map[str1[R]] >= 0) { + all--; + } + if (all == 0) { + while (map[str1[L]] < 0) { + map[str1[L++]]++; + } + minLen = Math.min(minLen, R - L + 1); + all++; + map[str1[L++]]++; + } + R++; + } + return minLen == Integer.MAX_VALUE ? 0 : minLen; + } + + // 测试链接 : https://leetcode.com/problems/minimum-window-substring/ + public static String minWindow(String s, String t) { + if (s.length() < t.length()) { + return ""; + } + char[] str = s.toCharArray(); + char[] target = t.toCharArray(); + int[] map = new int[256]; + for (char cha : target) { + map[cha]++; + } + int all = target.length; + int L = 0; + int R = 0; + int minLen = Integer.MAX_VALUE; + int ansl = -1; + int ansr = -1; + while (R != str.length) { + map[str[R]]--; + if (map[str[R]] >= 0) { + all--; + } + if (all == 0) { + while (map[str[L]] < 0) { + map[str[L++]]++; + } + if (minLen > R - L + 1) { + minLen = R - L + 1; + ansl = L; + ansr = R; + } + all++; + map[str[L++]]++; + } + R++; + } + return minLen == Integer.MAX_VALUE ? "" : s.substring(ansl, ansr + 1); + } + +} diff --git a/MCA算法突击课/第01期_mca_07_about_monotonicity/Code08_SplitArrayLargestSum.java b/MCA算法突击课/第01期_mca_07_about_monotonicity/Code08_SplitArrayLargestSum.java new file mode 100644 index 0000000..2da420c --- /dev/null +++ b/MCA算法突击课/第01期_mca_07_about_monotonicity/Code08_SplitArrayLargestSum.java @@ -0,0 +1,49 @@ +package 第01期_mca_07_about_monotonicity; + +// leetcode原题 +// 测试链接:https://leetcode.com/problems/split-array-largest-sum/ +public class Code08_SplitArrayLargestSum { + + public static int splitArray3(int[] nums, int M) { + long sum = 0; + for (int i = 0; i < nums.length; i++) { + sum += nums[i]; + } + // 0 ~ sum + long l = 0; + long r = sum; + long ans = 0; + while (l <= r) { + long limit = (l + r) / 2; + long cur = f(nums, limit); + if (cur <= M) { + ans = limit; + r = limit - 1; + } else { + l = limit + 1; + } + } + return (int) ans; + } + + public static int f(int[] arr, long limit) { + for (int i = 0; i < arr.length; i++) { + if (arr[i] > limit) { + return Integer.MAX_VALUE; + } + } + int parts = 1; + int all = arr[0]; + for (int i = 1; i < arr.length; i++) { + if (all + arr[i] > limit) { + parts++; + all = arr[i]; + } else { + all += arr[i]; + } + } + return parts; + } + + +} diff --git a/MCA算法突击课/第01期_mca_07_about_monotonicity/Code09_MonotonousStack.java b/MCA算法突击课/第01期_mca_07_about_monotonicity/Code09_MonotonousStack.java new file mode 100644 index 0000000..cc39aa8 --- /dev/null +++ b/MCA算法突击课/第01期_mca_07_about_monotonicity/Code09_MonotonousStack.java @@ -0,0 +1,165 @@ +package 第01期_mca_07_about_monotonicity; + +import java.util.List; +import java.util.ArrayList; +import java.util.Stack; + +public class Code09_MonotonousStack { + + // arr = [ 3, 1, 2, 3] + // 0 1 2 3 + // [ + // 0 : [-1, 1] + // 1 : [-1, -1] + // 2 : [ 1, -1] + // 3 : [ 2, -1] + // ] + public static int[][] getNearLessNoRepeat(int[] arr) { + int[][] res = new int[arr.length][2]; + // 只存位置! + Stack stack = new Stack<>(); + for (int i = 0; i < arr.length; i++) { // 当遍历到i位置的数,arr[i] + while (!stack.isEmpty() && arr[stack.peek()] > arr[i]) { + int j = stack.pop(); + int leftLessIndex = stack.isEmpty() ? -1 : stack.peek(); + res[j][0] = leftLessIndex; + res[j][1] = i; + } + stack.push(i); + } + while (!stack.isEmpty()) { + int j = stack.pop(); + int leftLessIndex = stack.isEmpty() ? -1 : stack.peek(); + res[j][0] = leftLessIndex; + res[j][1] = -1; + } + return res; + } + + public static int[][] getNearLess(int[] arr) { + int[][] res = new int[arr.length][2]; + Stack> stack = new Stack<>(); + for (int i = 0; i < arr.length; i++) { // i -> arr[i] 进栈 + while (!stack.isEmpty() && arr[stack.peek().get(0)] > arr[i]) { + List popIs = stack.pop(); + int leftLessIndex = stack.isEmpty() ? -1 : stack.peek().get(stack.peek().size() - 1); + for (Integer popi : popIs) { + res[popi][0] = leftLessIndex; + res[popi][1] = i; + } + } + if (!stack.isEmpty() && arr[stack.peek().get(0)] == arr[i]) { + stack.peek().add(Integer.valueOf(i)); + } else { + ArrayList list = new ArrayList<>(); + list.add(i); + stack.push(list); + } + } + while (!stack.isEmpty()) { + List popIs = stack.pop(); + int leftLessIndex = stack.isEmpty() ? -1 : stack.peek().get(stack.peek().size() - 1); + for (Integer popi : popIs) { + res[popi][0] = leftLessIndex; + res[popi][1] = -1; + } + } + return res; + } + + // for test + public static int[] getRandomArrayNoRepeat(int size) { + int[] arr = new int[(int) (Math.random() * size) + 1]; + for (int i = 0; i < arr.length; i++) { + arr[i] = i; + } + for (int i = 0; i < arr.length; i++) { + int swapIndex = (int) (Math.random() * arr.length); + int tmp = arr[swapIndex]; + arr[swapIndex] = arr[i]; + arr[i] = tmp; + } + return arr; + } + + // for test + public static int[] getRandomArray(int size, int max) { + int[] arr = new int[(int) (Math.random() * size) + 1]; + for (int i = 0; i < arr.length; i++) { + arr[i] = (int) (Math.random() * max) - (int) (Math.random() * max); + } + return arr; + } + + // for test + public static int[][] rightWay(int[] arr) { + int[][] res = new int[arr.length][2]; + for (int i = 0; i < arr.length; i++) { + int leftLessIndex = -1; + int rightLessIndex = -1; + int cur = i - 1; + while (cur >= 0) { + if (arr[cur] < arr[i]) { + leftLessIndex = cur; + break; + } + cur--; + } + cur = i + 1; + while (cur < arr.length) { + if (arr[cur] < arr[i]) { + rightLessIndex = cur; + break; + } + cur++; + } + res[i][0] = leftLessIndex; + res[i][1] = rightLessIndex; + } + return res; + } + + // for test + public static boolean isEqual(int[][] res1, int[][] res2) { + if (res1.length != res2.length) { + return false; + } + for (int i = 0; i < res1.length; i++) { + if (res1[i][0] != res2[i][0] || res1[i][1] != res2[i][1]) { + return false; + } + } + + return true; + } + + // for test + public static void printArray(int[] arr) { + for (int i = 0; i < arr.length; i++) { + System.out.print(arr[i] + " "); + } + System.out.println(); + } + + public static void main(String[] args) { + int size = 10; + int max = 20; + int testTimes = 2000000; + System.out.println("测试开始"); + for (int i = 0; i < testTimes; i++) { + int[] arr1 = getRandomArrayNoRepeat(size); + int[] arr2 = getRandomArray(size, max); + if (!isEqual(getNearLessNoRepeat(arr1), rightWay(arr1))) { + System.out.println("Oops!"); + printArray(arr1); + break; + } + if (!isEqual(getNearLess(arr2), rightWay(arr2))) { + System.out.println("Oops!"); + printArray(arr2); + break; + } + } + System.out.println("测试结束"); + } +} diff --git a/MCA算法突击课/第01期_mca_07_about_monotonicity/Code10_LargestRectangleInHistogram.java b/MCA算法突击课/第01期_mca_07_about_monotonicity/Code10_LargestRectangleInHistogram.java new file mode 100644 index 0000000..3123d0c --- /dev/null +++ b/MCA算法突击课/第01期_mca_07_about_monotonicity/Code10_LargestRectangleInHistogram.java @@ -0,0 +1,58 @@ +package 第01期_mca_07_about_monotonicity; + +import java.util.Stack; + +// 测试链接:https://leetcode.com/problems/largest-rectangle-in-histogram +public class Code10_LargestRectangleInHistogram { + + public static int largestRectangleArea1(int[] height) { + if (height == null || height.length == 0) { + return 0; + } + int maxArea = 0; + Stack stack = new Stack(); + for (int i = 0; i < height.length; i++) { + while (!stack.isEmpty() && height[i] <= height[stack.peek()]) { + int j = stack.pop(); + int k = stack.isEmpty() ? -1 : stack.peek(); + int curArea = (i - k - 1) * height[j]; + maxArea = Math.max(maxArea, curArea); + } + stack.push(i); + } + while (!stack.isEmpty()) { + int j = stack.pop(); + int k = stack.isEmpty() ? -1 : stack.peek(); + int curArea = (height.length - k - 1) * height[j]; + maxArea = Math.max(maxArea, curArea); + } + return maxArea; + } + + public static int largestRectangleArea2(int[] height) { + if (height == null || height.length == 0) { + return 0; + } + int N = height.length; + int[] stack = new int[N]; + int si = -1; + int maxArea = 0; + for (int i = 0; i < height.length; i++) { + while (si != -1 && height[i] <= height[stack[si]]) { + int j = stack[si--]; + int k = si == -1 ? -1 : stack[si]; + int curArea = (i - k - 1) * height[j]; + maxArea = Math.max(maxArea, curArea); + } + stack[++si] = i; + } + while (si != -1) { + int j = stack[si--]; + int k = si == -1 ? -1 : stack[si]; + int curArea = (height.length - k - 1) * height[j]; + maxArea = Math.max(maxArea, curArea); + } + return maxArea; + } + +} diff --git a/MCA算法突击课/第01期_mca_08_dp/Code01_Knapsack.java b/MCA算法突击课/第01期_mca_08_dp/Code01_Knapsack.java new file mode 100644 index 0000000..8067ae9 --- /dev/null +++ b/MCA算法突击课/第01期_mca_08_dp/Code01_Knapsack.java @@ -0,0 +1,158 @@ +package 第01期_mca_08_dp; + +public class Code01_Knapsack { + + // weight和value数组,等长! + public static int maxValue1(int[] weight, int[] value, int bagLimit) { + return f1(weight, value, 0, bagLimit); + } + + // 0....n-1 + // 0...index-1 已经不能选了! + // index...n-1这些货!自由挑选 + // 背包还剩下多少容量,rest,自由挑选是不能超过rest的! + // index...n-1,在符合要求的情况下,最大价值能达到多少 + public static int f1(int[] weight, int[] value, int index, int rest) { + if (rest < 0) { // 剩余的负重是负数,说明之前的选择是错误的! + return -1; // 无效解! + } + // rest >= 0 + if (index == weight.length) { // 没货了! + return 0; + } + // 既有负重,又有货物 + // 第一种选择:当前index位置的货,没要 + int p1 = f1(weight, value, index + 1, rest); + // 第二种选择:当前index位置的货,要! + int p2 = -1; + int next = f1(weight, value, index + 1, rest - weight[index]); + if (next != -1) { + p2 = value[index] + next; + } + return Math.max(p1, p2); + } + + + // weight和value数组,等长! + public static int maxValue2(int[] weight, int[] value, int bagLimit) { + int n = weight.length; + // index: 0...n + // bag : 100 0..100 + int[][] dp = new int[n+1][bagLimit+1]; + for(int i = 0; i<= n;i++) { + for(int j = 0 ; j <= bagLimit;j++) { + dp[i][j] = -2; + } + } + return f2(weight, value, 0, bagLimit, dp); + } + + public static int f2(int[] weight, int[] value, int index, int rest, int[][] dp) { + if (rest < 0) { + return -1; + } + if(dp[index][rest] != -2) { // 之前算过! + return dp[index][rest]; + } + // 缓存没命中! + int ans = 0; + if(index == weight.length) { + ans = 0; + } else { + int p1 = f2(weight, value, index + 1, rest, dp); + int p2 = -1; + int next = f2(weight, value, index + 1, rest - weight[index], dp); + if (next != -1) { + p2 = value[index] + next; + } + ans = Math.max(p1, p2); + } + dp[index][rest] = ans; + return ans; + } + + + + // 所有的货,重量和价值,都在w和v数组里 + // 为了方便,其中没有负数 + // bag背包容量,不能超过这个载重 + // 返回:不超重的情况下,能够得到的最大价值 +// public static int maxValue(int[] w, int[] v, int bag) { +// if (w == null || v == null || w.length != v.length || w.length == 0) { +// return 0; +// } +// // 尝试函数! +// return process(w, v, 0, bag); +// } +// +// // index 0~N +// // rest 负~bag +// public static int process(int[] w, int[] v, int index, int rest) { +// if (rest < 0) { +// return -1; +// } +// if (index == w.length) { +// return 0; +// } +// int p1 = process(w, v, index + 1, rest); +// int p2 = 0; +// int next = process(w, v, index + 1, rest - w[index]); +// if (next != -1) { +// p2 = v[index] + next; +// } +// return Math.max(p1, p2); +// } +// +// public static int dp(int[] w, int[] v, int bag) { +// if (w == null || v == null || w.length != v.length || w.length == 0) { +// return 0; +// } +// int N = w.length; +// int[][] dp = new int[N + 1][bag + 1]; +// for (int index = N - 1; index >= 0; index--) { +// for (int rest = 0; rest <= bag; rest++) { +// int p1 = dp[index + 1][rest]; +// int p2 = 0; +// int next = rest - w[index] < 0 ? -1 : dp[index + 1][rest - w[index]]; +// if (next != -1) { +// p2 = v[index] + next; +// } +// dp[index][rest] = Math.max(p1, p2); +// } +// } +// return dp[0][bag]; +// } + + public static int[] randomArray(int len, int value) { + int[] arr = new int[len]; + for(int i = 0 ; i< len;i++) { + arr[i] = (int)(Math.random() * value) + 1; + } + return arr; + } + + public static void main(String[] args) { + int len = 30; + int value = 50; + + int[] weight = randomArray(len, value); + int[] values = randomArray(len, value ); + int bag = 2000; + + long start; + long end; + + // 2 ^ 30 + start = System.currentTimeMillis(); + System.out.println(maxValue1(weight, values, bag)); + end = System.currentTimeMillis(); + System.out.println("运行时间(毫秒):" + (end - start)); + + // 30 * 2000 -> 60000 + start = System.currentTimeMillis(); + System.out.println(maxValue2(weight, values, bag)); + end = System.currentTimeMillis(); + System.out.println("运行时间(毫秒):" + (end - start)); + } + +} diff --git a/MCA算法突击课/第01期_mca_08_dp/Code02_SumWays.java b/MCA算法突击课/第01期_mca_08_dp/Code02_SumWays.java new file mode 100644 index 0000000..f770854 --- /dev/null +++ b/MCA算法突击课/第01期_mca_08_dp/Code02_SumWays.java @@ -0,0 +1,27 @@ +package 第01期_mca_08_dp; + +public class Code02_SumWays { + + // arr中都是正数,sum也是正数 + // 求组成sum的方法数量 + public static int sumWays(int[] arr, int sum) { + int n = arr.length; + if (n == 0) { + return sum == 0 ? 1 : 0; + } + int[][] dp = new int[n][sum + 1]; + for (int i = 0; i < n; i++) { + dp[i][0] = 1; + } + if (arr[0] <= sum) { + dp[0][arr[0]] = 1; + } + for (int i = 1; i < n; i++) { + for (int j = 1; j <= sum; j++) { + dp[i][j] = dp[i - 1][j] + (j - arr[i] >= 0 ? dp[i - 1][j - arr[i]] : 0); + } + } + return dp[n - 1][sum]; + } + +} diff --git a/MCA算法突击课/第01期_mca_08_dp/Code03_LongestSubstringWithoutRepeatingCharacters.java b/MCA算法突击课/第01期_mca_08_dp/Code03_LongestSubstringWithoutRepeatingCharacters.java new file mode 100644 index 0000000..350da32 --- /dev/null +++ b/MCA算法突击课/第01期_mca_08_dp/Code03_LongestSubstringWithoutRepeatingCharacters.java @@ -0,0 +1,27 @@ +package 第01期_mca_08_dp; + +// 本题测试链接 : https://leetcode.com/problems/longest-substring-without-repeating-characters/ +public class Code03_LongestSubstringWithoutRepeatingCharacters { + + public static int lengthOfLongestSubstring(String s) { + if (s == null || s.equals("")) { + return 0; + } + char[] str = s.toCharArray(); + int[] map = new int[256]; + for (int i = 0; i < 256; i++) { + map[i] = -1; + } + map[str[0]] = 0; + int N = str.length; + int ans = 1; + int pre = 1; + for (int i = 1; i < N; i++) { + pre = Math.min(i - map[str[i]], pre + 1); + ans = Math.max(ans, pre); + map[str[i]] = i; + } + return ans; + } + +} diff --git a/MCA算法突击课/第01期_mca_08_dp/Code04_SubArrayMaxSum.java b/MCA算法突击课/第01期_mca_08_dp/Code04_SubArrayMaxSum.java new file mode 100644 index 0000000..e2a6747 --- /dev/null +++ b/MCA算法突击课/第01期_mca_08_dp/Code04_SubArrayMaxSum.java @@ -0,0 +1,35 @@ +package 第01期_mca_08_dp; + +// 本题测试链接 : https://leetcode.com/problems/maximum-subarray/ +public class Code04_SubArrayMaxSum { + + public static int maxSubArray(int[] arr) { + if (arr == null || arr.length == 0) { + return 0; + } + int max = Integer.MIN_VALUE; + int cur = 0; + for (int i = 0; i < arr.length; i++) { + cur += arr[i]; + max = Math.max(max, cur); + cur = cur < 0 ? 0 : cur; + } + return max; + } + + public static int maxSubArray2(int[] arr) { + if (arr == null || arr.length == 0) { + return 0; + } + // 上一步,dp的值 + // dp[0] + int pre = arr[0]; + int max = arr[0]; + for (int i = 1; i < arr.length; i++) { + pre = Math.max(arr[i], arr[i] + pre); + max = Math.max(max, pre); + } + return max; + } + +} diff --git a/MCA算法突击课/第01期_mca_08_dp/Code05_CardsInLine.java b/MCA算法突击课/第01期_mca_08_dp/Code05_CardsInLine.java new file mode 100644 index 0000000..08a1b6e --- /dev/null +++ b/MCA算法突击课/第01期_mca_08_dp/Code05_CardsInLine.java @@ -0,0 +1,208 @@ +package 第01期_mca_08_dp; + +public class Code05_CardsInLine { + + public static int getWinnerScore(int[] arr) { + int xian = xian(arr, 0, arr.length - 1); + int hou = hou(arr, 0, arr.length - 1); + return Math.max(xian, hou); + } + + // 目前,是在arr[L...R]这个范围上玩牌! + // 返回,先手最终的最大得分 + public static int xian(int[] arr, int L, int R) { + if (L == R) { + return arr[L]; + } + if (L == R - 1) { + return Math.max(arr[L], arr[R]); + } + // L...R上,不止两张牌! + // 可能性1:拿走L位置的牌 + int p1 = arr[L] + hou(arr, L + 1, R); + // 可能性2:拿走R位置的牌 + int p2 = arr[R] + hou(arr, L, R - 1); + return Math.max(p1, p2); + } + + // 目前,是在arr[L...R]这个范围上玩牌! + // 返回,后手最终的最大得分 + public static int hou(int[] arr, int L, int R) { + if (L == R) { + return 0; + } + if (L == R - 1) { + return Math.min(arr[L], arr[R]); + } + // L..R上,不止两张牌 + // 后手! + // 可能性1:对手,先手,拿走L位置的牌,接下来!你就可以在L+1..R上先手了! + int p1 = xian(arr, L + 1, R); + // 可能性2:对手,先手,拿走R位置的牌,接下来!你就可以在L..R-1上先手了! + int p2 = xian(arr, L, R - 1); + return Math.min(p1, p2); + } + + public static int getWinnerScore2(int[] arr) { + int n = arr.length; + int[][] dpxian = new int[n][n]; + int[][] dphou = new int[n][n]; + for (int i = 0; i < n; i++) { + for (int j = 0; j < n; j++) { + dpxian[i][j] = -1; + dphou[i][j] = -1; + } + } + int xian = xian2(arr, 0, arr.length - 1, dpxian, dphou); + int hou = hou2(arr, 0, arr.length - 1, dpxian, dphou); + return Math.max(xian, hou); + } + + public static int xian2(int[] arr, int L, int R, int[][] dpxian, int[][] dphou) { + if (dpxian[L][R] != -1) { + return dpxian[L][R]; + } + int ans = 0; + if (L == R) { + ans = arr[L]; + } else if (L == R - 1) { + ans = Math.max(arr[L], arr[R]); + } else { + int p1 = arr[L] + hou2(arr, L + 1, R, dpxian, dphou); + int p2 = arr[R] + hou2(arr, L, R - 1, dpxian, dphou); + ans = Math.max(p1, p2); + } + dpxian[L][R] = ans; + return ans; + } + + public static int hou2(int[] arr, int L, int R, int[][] dpxian, int[][] dphou) { + if (dphou[L][R] != -1) { + return dphou[L][R]; + } + int ans = 0; + if (L == R) { + ans = 0; + } else if (L == R - 1) { + ans = Math.min(arr[L], arr[R]); + } else { + int p1 = xian2(arr, L + 1, R, dpxian, dphou); + int p2 = xian2(arr, L, R - 1, dpxian, dphou); + ans = Math.min(p1, p2); + } + dphou[L][R] = ans; + return ans; + } + +// // 根据规则,返回获胜者的分数 +// public static int win1(int[] arr) { +// if (arr == null || arr.length == 0) { +// return 0; +// } +// int first = f1(arr, 0, arr.length - 1); +// int second = g1(arr, 0, arr.length - 1); +// return Math.max(first, second); +// } +// +// // arr[L..R],先手获得的最好分数返回 +// public static int f1(int[] arr, int L, int R) { +// if (L == R) { +// return arr[L]; +// } +// int p1 = arr[L] + g1(arr, L + 1, R); +// int p2 = arr[R] + g1(arr, L, R - 1); +// return Math.max(p1, p2); +// } +// +// // // arr[L..R],后手获得的最好分数返回 +// public static int g1(int[] arr, int L, int R) { +// if (L == R) { +// return 0; +// } +// int p1 = f1(arr, L + 1, R); // 对手拿走了L位置的数 +// int p2 = f1(arr, L, R - 1); // 对手拿走了R位置的数 +// return Math.min(p1, p2); +// } +// +// public static int win2(int[] arr) { +// if (arr == null || arr.length == 0) { +// return 0; +// } +// int N = arr.length; +// int[][] fmap = new int[N][N]; +// int[][] gmap = new int[N][N]; +// for (int i = 0; i < N; i++) { +// for (int j = 0; j < N; j++) { +// fmap[i][j] = -1; +// gmap[i][j] = -1; +// } +// } +// int first = f2(arr, 0, arr.length - 1, fmap, gmap); +// int second = g2(arr, 0, arr.length - 1, fmap, gmap); +// return Math.max(first, second); +// } +// +// // arr[L..R],先手获得的最好分数返回 +// public static int f2(int[] arr, int L, int R, int[][] fmap, int[][] gmap) { +// if (fmap[L][R] != -1) { +// return fmap[L][R]; +// } +// int ans = 0; +// if (L == R) { +// ans = arr[L]; +// } else { +// int p1 = arr[L] + g2(arr, L + 1, R, fmap, gmap); +// int p2 = arr[R] + g2(arr, L, R - 1, fmap, gmap); +// ans = Math.max(p1, p2); +// } +// fmap[L][R] = ans; +// return ans; +// } +// +// // // arr[L..R],后手获得的最好分数返回 +// public static int g2(int[] arr, int L, int R, int[][] fmap, int[][] gmap) { +// if (gmap[L][R] != -1) { +// return gmap[L][R]; +// } +// int ans = 0; +// if (L != R) { +// int p1 = f2(arr, L + 1, R, fmap, gmap); // 对手拿走了L位置的数 +// int p2 = f2(arr, L, R - 1, fmap, gmap); // 对手拿走了R位置的数 +// ans = Math.min(p1, p2); +// } +// gmap[L][R] = ans; +// return ans; +// } +// +// public static int win3(int[] arr) { +// if (arr == null || arr.length == 0) { +// return 0; +// } +// int N = arr.length; +// int[][] fmap = new int[N][N]; +// int[][] gmap = new int[N][N]; +// for (int i = 0; i < N; i++) { +// fmap[i][i] = arr[i]; +// } +// for (int startCol = 1; startCol < N; startCol++) { +// int L = 0; +// int R = startCol; +// while (R < N) { +// fmap[L][R] = Math.max(arr[L] + gmap[L + 1][R], arr[R] + gmap[L][R - 1]); +// gmap[L][R] = Math.min(fmap[L + 1][R], fmap[L][R - 1]); +// L++; +// R++; +// } +// } +// return Math.max(fmap[0][N - 1], gmap[0][N - 1]); +// } + + public static void main(String[] args) { + int[] arr = { 5, 7, 4, 5, 8, 1, 6, 0, 3, 4, 6, 1, 7 }; + + System.out.println(getWinnerScore(arr)); + System.out.println(getWinnerScore2(arr)); + + } + +} diff --git a/MCA算法突击课/第01期_mca_08_dp/Code06_BurstBalloons.java b/MCA算法突击课/第01期_mca_08_dp/Code06_BurstBalloons.java new file mode 100644 index 0000000..0dc7de2 --- /dev/null +++ b/MCA算法突击课/第01期_mca_08_dp/Code06_BurstBalloons.java @@ -0,0 +1,180 @@ +package 第01期_mca_08_dp; + +// 本题测试链接 : https://leetcode.com/problems/burst-balloons/ +public class Code06_BurstBalloons { + + public static int maxScore1(int[] arr) { + int n = arr.length; + int[] help = new int[n + 2]; + help[0] = 1; + help[n + 1] = 1; + for (int i = 1; i <= n; i++) { + help[i] = arr[i - 1]; + } + // [3,2,1,4,5] -> [1,3,2,1,4,5,1]; + // 0 1 2 3 4 1 2 3 4 5 + return process1(help, 1, n); + } + + // arr[L...R]上打爆气球,返回最大得分! + // 潜台词 : arr[L-1]这个气球,一定没爆!arr[R+1]这个气球,一定也没爆! + public static int process1(int[] arr, int L, int R) { + if (L == R) { + return arr[L - 1] * arr[L] * arr[R + 1]; + } + // 不止一个气球 + // 分析可能性 + // 1)最后打爆L位置的气球 + int p1 = process1(arr, L + 1, R) + arr[L - 1] * arr[L] * arr[R + 1]; + // 1)最后打爆R位置的气球 + int p2 = process1(arr, L, R - 1) + arr[L - 1] * arr[R] * arr[R + 1]; + int ans = Math.max(p1, p2); + // 尝试中间每一个气球都最后打爆! + for (int mid = L + 1; mid < R; mid++) { + // arr[mid](最后打爆,一定要最后打爆!) + int cur = process1(arr, L, mid - 1) + process1(arr, mid + 1, R) + arr[L - 1] * arr[mid] * arr[R + 1]; + ans = Math.max(ans, cur); + } + return ans; + } + + public static int maxScore2(int[] arr) { + int n = arr.length; + int[] help = new int[n + 2]; + help[0] = 1; + help[n + 1] = 1; + for (int i = 1; i <= n; i++) { + help[i] = arr[i - 1]; + } + int[][] dp = new int[n + 1][n + 1]; + for (int i = 0; i <= n; i++) { + for (int j = 0; j <= n; j++) { + dp[i][j] = -1; + } + } + return process2(help, 1, n, dp); + } + + public static int process2(int[] arr, int L, int R, int[][] dp) { + if (dp[L][R] != -1) { + return dp[L][R]; + } + + int ans = 0; + if (L == R) { + ans = arr[L - 1] * arr[L] * arr[R + 1]; + } else { + int p1 = process2(arr, L + 1, R, dp) + arr[L - 1] * arr[L] * arr[R + 1]; + int p2 = process2(arr, L, R - 1,dp) + arr[L - 1] * arr[R] * arr[R + 1]; + ans = Math.max(p1, p2); + for (int mid = L + 1; mid < R; mid++) { + int cur = process2(arr, L, mid - 1,dp) + process2(arr, mid + 1, R,dp) + arr[L - 1] * arr[mid] * arr[R + 1]; + ans = Math.max(ans, cur); + } + } + dp[L][R] = ans; + return ans; + } + + public static int maxCoins0(int[] arr) { + // [3,2,1,3] + // [1,3,2,1,3,1] + int N = arr.length; + int[] help = new int[N + 2]; + for (int i = 0; i < N; i++) { + help[i + 1] = arr[i]; + } + help[0] = 1; + help[N + 1] = 1; + return func(help, 1, N); + } + + // L-1位置,和R+1位置,永远不越界,并且,[L-1] 和 [R+1] 一定没爆呢! + // 返回,arr[L...R]打爆所有气球,最大得分是什么 + public static int func(int[] arr, int L, int R) { + if (L == R) { + return arr[L - 1] * arr[L] * arr[R + 1]; + } + // 尝试每一种情况,最后打爆的气球,是什么位置 + // L...R + // L位置的气球,最后打爆 + int max = func(arr, L + 1, R) + arr[L - 1] * arr[L] * arr[R + 1]; + // R位置的气球,最后打爆 + max = Math.max(max, func(arr, L, R - 1) + arr[L - 1] * arr[R] * arr[R + 1]); + // 尝试所有L...R,中间的位置,(L,R) + for (int i = L + 1; i < R; i++) { + // i位置的气球,最后打爆 + int left = func(arr, L, i - 1); + int right = func(arr, i + 1, R); + int last = arr[L - 1] * arr[i] * arr[R + 1]; + int cur = left + right + last; + max = Math.max(max, cur); + } + return max; + } + + public static int maxCoins1(int[] arr) { + if (arr == null || arr.length == 0) { + return 0; + } + if (arr.length == 1) { + return arr[0]; + } + int N = arr.length; + int[] help = new int[N + 2]; + help[0] = 1; + help[N + 1] = 1; + for (int i = 0; i < N; i++) { + help[i + 1] = arr[i]; + } + return process(help, 1, N); + } + + // 打爆arr[L..R]范围上的所有气球,返回最大的分数 + // 假设arr[L-1]和arr[R+1]一定没有被打爆 + public static int process(int[] arr, int L, int R) { + if (L == R) {// 如果arr[L..R]范围上只有一个气球,直接打爆即可 + return arr[L - 1] * arr[L] * arr[R + 1]; + } + // 最后打爆arr[L]的方案,和最后打爆arr[R]的方案,先比较一下 + int max = Math.max(arr[L - 1] * arr[L] * arr[R + 1] + process(arr, L + 1, R), + arr[L - 1] * arr[R] * arr[R + 1] + process(arr, L, R - 1)); + // 尝试中间位置的气球最后被打爆的每一种方案 + for (int i = L + 1; i < R; i++) { + max = Math.max(max, arr[L - 1] * arr[i] * arr[R + 1] + process(arr, L, i - 1) + process(arr, i + 1, R)); + } + return max; + } + + public static int maxCoins2(int[] arr) { + if (arr == null || arr.length == 0) { + return 0; + } + if (arr.length == 1) { + return arr[0]; + } + int N = arr.length; + int[] help = new int[N + 2]; + help[0] = 1; + help[N + 1] = 1; + for (int i = 0; i < N; i++) { + help[i + 1] = arr[i]; + } + int[][] dp = new int[N + 2][N + 2]; + for (int i = 1; i <= N; i++) { + dp[i][i] = help[i - 1] * help[i] * help[i + 1]; + } + for (int L = N; L >= 1; L--) { + for (int R = L + 1; R <= N; R++) { + int ans = help[L - 1] * help[L] * help[R + 1] + dp[L + 1][R]; + ans = Math.max(ans, help[L - 1] * help[R] * help[R + 1] + dp[L][R - 1]); + for (int i = L + 1; i < R; i++) { + ans = Math.max(ans, help[L - 1] * help[i] * help[R + 1] + dp[L][i - 1] + dp[i + 1][R]); + } + dp[L][R] = ans; + } + } + return dp[1][N]; + } + +} diff --git a/MCA算法突击课/第01期_mca_08_dp/Code07_LongestCommonSubsequence.java b/MCA算法突击课/第01期_mca_08_dp/Code07_LongestCommonSubsequence.java new file mode 100644 index 0000000..e84f29a --- /dev/null +++ b/MCA算法突击课/第01期_mca_08_dp/Code07_LongestCommonSubsequence.java @@ -0,0 +1,67 @@ +package 第01期_mca_08_dp; + +// 这个问题leetcode上可以直接测 +// 链接:https://leetcode.com/problems/longest-common-subsequence/ +public class Code07_LongestCommonSubsequence { + + public static int longestCommonSubsequence1(String s1, String s2) { + if (s1 == null || s2 == null || s1.length() == 0 || s2.length() == 0) { + return 0; + } + char[] str1 = s1.toCharArray(); + char[] str2 = s2.toCharArray(); + // 尝试 + return process1(str1, str2, str1.length - 1, str2.length - 1); + } + + public static int process1(char[] str1, char[] str2, int i, int j) { + if (i == 0 && j == 0) { + return str1[i] == str2[j] ? 1 : 0; + } else if (i == 0) { + if (str1[i] == str2[j]) { + return 1; + } else { + return process1(str1, str2, i, j - 1); + } + } else if (j == 0) { + if (str1[i] == str2[j]) { + return 1; + } else { + return process1(str1, str2, i - 1, j); + } + } else { // i != 0 && j != 0 + int p1 = process1(str1, str2, i - 1, j); + int p2 = process1(str1, str2, i, j - 1); + int p3 = str1[i] == str2[j] ? (1 + process1(str1, str2, i - 1, j - 1)) : 0; + return Math.max(p1, Math.max(p2, p3)); + } + } + + public static int longestCommonSubsequence2(String s1, String s2) { + if (s1 == null || s2 == null || s1.length() == 0 || s2.length() == 0) { + return 0; + } + char[] str1 = s1.toCharArray(); + char[] str2 = s2.toCharArray(); + int N = str1.length; + int M = str2.length; + int[][] dp = new int[N][M]; + dp[0][0] = str1[0] == str2[0] ? 1 : 0; + for (int j = 1; j < M; j++) { + dp[0][j] = str1[0] == str2[j] ? 1 : dp[0][j - 1]; + } + for (int i = 1; i < N; i++) { + dp[i][0] = str1[i] == str2[0] ? 1 : dp[i - 1][0]; + } + for (int i = 1; i < N; i++) { + for (int j = 1; j < M; j++) { + int p1 = dp[i - 1][j]; + int p2 = dp[i][j - 1]; + int p3 = str1[i] == str2[j] ? (1 + dp[i - 1][j - 1]) : 0; + dp[i][j] = Math.max(p1, Math.max(p2, p3)); + } + } + return dp[N - 1][M - 1]; + } + +} diff --git a/MCA算法突击课/第01期_mca_08_dp/Code08_EditCost.java b/MCA算法突击课/第01期_mca_08_dp/Code08_EditCost.java new file mode 100644 index 0000000..d795e55 --- /dev/null +++ b/MCA算法突击课/第01期_mca_08_dp/Code08_EditCost.java @@ -0,0 +1,89 @@ +package 第01期_mca_08_dp; + +public class Code08_EditCost { + + public static int minCost1(String s1, String s2, int ic, int dc, int rc) { + if (s1 == null || s2 == null) { + return 0; + } + char[] str1 = s1.toCharArray(); + char[] str2 = s2.toCharArray(); + int N = str1.length + 1; + int M = str2.length + 1; + int[][] dp = new int[N][M]; + // dp[0][0] = 0 + for (int i = 1; i < N; i++) { + dp[i][0] = dc * i; + } + for (int j = 1; j < M; j++) { + dp[0][j] = ic * j; + } + for (int i = 1; i < N; i++) { + for (int j = 1; j < M; j++) { + dp[i][j] = dp[i - 1][j - 1] + (str1[i - 1] == str2[j - 1] ? 0 : rc); + dp[i][j] = Math.min(dp[i][j], dp[i][j - 1] + ic); + dp[i][j] = Math.min(dp[i][j], dp[i - 1][j] + dc); + } + } + return dp[N - 1][M - 1]; + } + + public static int minCost2(String str1, String str2, int ic, int dc, int rc) { + if (str1 == null || str2 == null) { + return 0; + } + char[] chs1 = str1.toCharArray(); + char[] chs2 = str2.toCharArray(); + char[] longs = chs1.length >= chs2.length ? chs1 : chs2; + char[] shorts = chs1.length < chs2.length ? chs1 : chs2; + if (chs1.length < chs2.length) { + int tmp = ic; + ic = dc; + dc = tmp; + } + int[] dp = new int[shorts.length + 1]; + for (int i = 1; i <= shorts.length; i++) { + dp[i] = ic * i; + } + for (int i = 1; i <= longs.length; i++) { + int pre = dp[0]; + dp[0] = dc * i; + for (int j = 1; j <= shorts.length; j++) { + int tmp = dp[j]; + if (longs[i - 1] == shorts[j - 1]) { + dp[j] = pre; + } else { + dp[j] = pre + rc; + } + dp[j] = Math.min(dp[j], dp[j - 1] + ic); + dp[j] = Math.min(dp[j], tmp + dc); + pre = tmp; + } + } + return dp[shorts.length]; + } + + public static void main(String[] args) { + String str1 = "ab12cd3"; + String str2 = "abcdf"; + System.out.println(minCost1(str1, str2, 5, 3, 2)); + System.out.println(minCost2(str1, str2, 5, 3, 2)); + + str1 = "abcdf"; + str2 = "ab12cd3"; + System.out.println(minCost1(str1, str2, 3, 2, 4)); + System.out.println(minCost2(str1, str2, 3, 2, 4)); + + str1 = ""; + str2 = "ab12cd3"; + System.out.println(minCost1(str1, str2, 1, 7, 5)); + System.out.println(minCost2(str1, str2, 1, 7, 5)); + + str1 = "abcdf"; + str2 = ""; + System.out.println(minCost1(str1, str2, 2, 9, 8)); + System.out.println(minCost2(str1, str2, 2, 9, 8)); + + } + +} diff --git a/MCA算法突击课/第01期_mca_08_dp/Code09_SplitApples.java b/MCA算法突击课/第01期_mca_08_dp/Code09_SplitApples.java new file mode 100644 index 0000000..e441ede --- /dev/null +++ b/MCA算法突击课/第01期_mca_08_dp/Code09_SplitApples.java @@ -0,0 +1,153 @@ +package 第01期_mca_08_dp; + +//有m个同样的苹果,认为苹果之间无差别 +//有n个同样的盘子,认为盘子之间也无差别 +//还有,比如5个苹果如果放进3个盘子, +//那么1、3、1和1、1、3和3、1、1的放置方法,也认为是一种方法 +//如上的设定下,返回有多少种放置方法 +//测试链接 : https://www.nowcoder.com/practice/bfd8234bb5e84be0b493656e390bdebf +//提交以下的code,提交时请把类名改成"Main" +import java.util.Arrays; +import java.util.Scanner; + +public class Code09_SplitApples { + + public static int test(int apples, int plates) { + int[][] dp = new int[apples + 1][plates + 1]; + for (int i = 0; i <= apples; i++) { + for (int j = 0; j <= plates; j++) { + dp[i][j] = -1; + } + } + return f(apples, plates, dp); + } + + public static int f(int apples, int plates, int[][] dp) { + if (dp[apples][plates] != -1) { + return dp[apples][plates]; + } + int ans = 0; + if (apples == 0) { + ans = 1; + } else if (plates == 0) { + ans = 0; + } else { + if (plates > apples) { + ans = f(apples, apples, dp); + } else { // apples >= plates; + ans = f(apples, plates - 1, dp) + f(apples - plates, plates, dp); + } + } + dp[apples][plates] = ans; + return ans; + } + + public static void main(String[] args) { + Scanner sc = new Scanner(System.in); + while (sc.hasNext()) { + int m = sc.nextInt(); + int n = sc.nextInt(); + int ways = ways3(m, n); + System.out.println(ways); + } + sc.close(); + } + + // 思路来自于分裂数问题 + // 体系学习班代码第22节,题目3,split number问题 + public static int ways1(int apples, int plates) { + return process1(1, apples, plates); + } + + // pre : 上一个盘子分到的苹果数量,当前的盘子分到的数量不能小于pre + // apples : 剩余的苹果数量 + // plates : 剩余的盘子数量 + // 在盘子够用的情况下,把苹果分完,有几种方法 + public static int process1(int pre, int apples, int plates) { + if (apples == 0) { + return 1; + } + // apples != 0 + if (plates == 0) { + return 0; + } + // apples != 0 && plates != 0 + if (pre > apples) { + return 0; + } + // apples != 0 && plates != 0 && pre <= apples + int way = 0; + // 之前的盘子分了3个苹果,现在还剩下8个苹果 + // 当前的盘子,可以装几个苹果:3、4、5、6、7、8 + for (int cur = pre; cur <= apples; cur++) { + way += process1(cur, apples - cur, plates - 1); + } + return way; + } + + // 新的尝试,最优解 + // 苹果有apples个,盘子有plates个 + // 返回有几种摆法 + // 如果苹果数为0,有1种摆法:什么也不摆 + // 如果苹果数不为0,但是盘子数为0,有0种摆法(做不到) + // 如果苹果数不为0,盘子数也不为0,进行如下的情况讨论: + // 假设苹果数为apples,盘子数为plates + // 可能性 1) apples < plates + // 这种情况下,一定有多余的盘子,这些盘子完全没用,所以砍掉 + // 后续是f(apples, apples) + // 可能性 2) apples >= plates + // 在可能性2)下,讨论摆法,有如下两种选择 + // 选择a) 不是所有的盘子都使用 + // 选择b) 就是所有的盘子都使用 + // 对于选择a),既然不是所有盘子都使用,那么后续就是f(apples, plates - 1) + // 意思是:既然不是所有盘子都使用,那盘子减少一个,然后继续讨论吧! + // 对于选择b),既然就是所有的盘子都使用,那么先把所有盘子都摆上1个苹果。 + // 剩余苹果数 = apples - plates + // 然后继续讨论,剩下的这些苹果,怎么摆进plates个盘子里, + // 所以后续是f(apples - plates, plates) + public static int ways2(int apples, int plates) { + if (apples == 0) { + return 1; + } + if (plates == 0) { + return 0; + } + if (plates > apples) { + return ways2(apples, apples); + } else { // apples >= plates; + return ways2(apples, plates - 1) + ways2(apples - plates, plates); + } + } + + // 上面最优解尝试的记忆化搜索版本 + public static int[][] dp = null; + + public static int ways3(int apples, int plates) { + if (dp == null) { + dp = new int[11][11]; + for (int i = 0; i <= 10; i++) { + Arrays.fill(dp[i], -1); + } + } + return process3(apples, plates, dp); + } + + public static int process3(int apples, int plates, int[][] dp) { + if (dp[apples][plates] != -1) { + return dp[apples][plates]; + } + int ans = 0; + if (apples == 0) { + ans = 1; + } else if (plates == 0) { + ans = 0; + } else if (plates > apples) { + ans = process3(apples, apples, dp); + } else { + ans = process3(apples, plates - 1, dp) + process3(apples - plates, plates, dp); + } + dp[apples][plates] = ans; + return ans; + } + +} \ No newline at end of file diff --git a/MCA算法突击课/第01期_mca_08_dp/Code10_HorseJump.java b/MCA算法突击课/第01期_mca_08_dp/Code10_HorseJump.java new file mode 100644 index 0000000..b98d373 --- /dev/null +++ b/MCA算法突击课/第01期_mca_08_dp/Code10_HorseJump.java @@ -0,0 +1,109 @@ +package 第01期_mca_08_dp; + +public class Code10_HorseJump { + + // 当前来到的位置是(x,y) + // 还剩下rest步需要跳 + // 跳完rest步,正好跳到a,b的方法数是多少? + // 10 * 9 + public static int jump(int a, int b, int k) { + return process(0, 0, k, a, b); + } + + public static int process(int x, int y, int rest, int a, int b) { + if (x < 0 || x > 9 || y < 0 || y > 8) { + return 0; + } + if (rest == 0) { + return (x == a && y == b) ? 1 : 0; + } + int ways = process(x + 2, y + 1, rest - 1, a, b); + ways += process(x + 1, y + 2, rest - 1, a, b); + ways += process(x - 1, y + 2, rest - 1, a, b); + ways += process(x - 2, y + 1, rest - 1, a, b); + ways += process(x - 2, y - 1, rest - 1, a, b); + ways += process(x - 1, y - 2, rest - 1, a, b); + ways += process(x + 1, y - 2, rest - 1, a, b); + ways += process(x + 2, y - 1, rest - 1, a, b); + return ways; + } + + public static int dp(int a, int b, int k) { + int[][][] dp = new int[10][9][k + 1]; + dp[a][b][0] = 1; + for (int rest = 1; rest <= k; rest++) { + for (int x = 0; x < 10; x++) { + for (int y = 0; y < 9; y++) { + int ways = pick(dp, x + 2, y + 1, rest - 1); + ways += pick(dp, x + 1, y + 2, rest - 1); + ways += pick(dp, x - 1, y + 2, rest - 1); + ways += pick(dp, x - 2, y + 1, rest - 1); + ways += pick(dp, x - 2, y - 1, rest - 1); + ways += pick(dp, x - 1, y - 2, rest - 1); + ways += pick(dp, x + 1, y - 2, rest - 1); + ways += pick(dp, x + 2, y - 1, rest - 1); + dp[x][y][rest] = ways; + } + } + } + return dp[0][0][k]; + } + + public static int pick(int[][][] dp, int x, int y, int rest) { + if (x < 0 || x > 9 || y < 0 || y > 8) { + return 0; + } + return dp[x][y][rest]; + } + + public static int ways(int a, int b, int step) { + return f(0, 0, step, a, b); + } + + public static int f(int i, int j, int step, int a, int b) { + if (i < 0 || i > 9 || j < 0 || j > 8) { + return 0; + } + if (step == 0) { + return (i == a && j == b) ? 1 : 0; + } + return f(i - 2, j + 1, step - 1, a, b) + f(i - 1, j + 2, step - 1, a, b) + f(i + 1, j + 2, step - 1, a, b) + + f(i + 2, j + 1, step - 1, a, b) + f(i + 2, j - 1, step - 1, a, b) + f(i + 1, j - 2, step - 1, a, b) + + f(i - 1, j - 2, step - 1, a, b) + f(i - 2, j - 1, step - 1, a, b); + + } + + public static int waysdp(int a, int b, int s) { + int[][][] dp = new int[10][9][s + 1]; + dp[a][b][0] = 1; + for (int step = 1; step <= s; step++) { // 按层来 + for (int i = 0; i < 10; i++) { + for (int j = 0; j < 9; j++) { + dp[i][j][step] = getValue(dp, i - 2, j + 1, step - 1) + getValue(dp, i - 1, j + 2, step - 1) + + getValue(dp, i + 1, j + 2, step - 1) + getValue(dp, i + 2, j + 1, step - 1) + + getValue(dp, i + 2, j - 1, step - 1) + getValue(dp, i + 1, j - 2, step - 1) + + getValue(dp, i - 1, j - 2, step - 1) + getValue(dp, i - 2, j - 1, step - 1); + } + } + } + return dp[0][0][s]; + } + + // 在dp表中,得到dp[i][j][step]的值,但如果(i,j)位置越界的话,返回0; + public static int getValue(int[][][] dp, int i, int j, int step) { + if (i < 0 || i > 9 || j < 0 || j > 8) { + return 0; + } + return dp[i][j][step]; + } + + public static void main(String[] args) { + int x = 7; + int y = 7; + int step = 10; + System.out.println(ways(x, y, step)); + System.out.println(dp(x, y, step)); + + System.out.println(jump(x, y, step)); + } +} diff --git a/MCA算法突击课/第01期_test/Code01_BestTimeToBuyAndSellStock.java b/MCA算法突击课/第01期_test/Code01_BestTimeToBuyAndSellStock.java new file mode 100644 index 0000000..d428af0 --- /dev/null +++ b/MCA算法突击课/第01期_test/Code01_BestTimeToBuyAndSellStock.java @@ -0,0 +1,21 @@ +package 第01期_test; + +// leetcode 121 +public class Code01_BestTimeToBuyAndSellStock { + + public static int maxProfit(int[] prices) { + if (prices == null || prices.length == 0) { + return 0; + } + // 必须在0时刻卖掉,[0] - [0] + int ans = 0; + // arr[0...0] + int min = prices[0]; + for (int i = 1; i < prices.length; i++) { + min = Math.min(min, prices[i]); + ans = Math.max(ans, prices[i] - min); + } + return ans; + } + +} diff --git a/MCA算法突击课/第01期_test/Code01_PalindromeSubsequence.java b/MCA算法突击课/第01期_test/Code01_PalindromeSubsequence.java new file mode 100644 index 0000000..c34891a --- /dev/null +++ b/MCA算法突击课/第01期_test/Code01_PalindromeSubsequence.java @@ -0,0 +1,121 @@ +package 第01期_test; + +// 测试链接:https://leetcode.com/problems/longest-palindromic-subsequence/ +public class Code01_PalindromeSubsequence { + + public static int lpsl1(String s) { + if (s == null || s.length() == 0) { + return 0; + } + char[] str = s.toCharArray(); + return f(str, 0, str.length - 1); + } + + // str[L..R]最长回文子序列长度返回 + public static int f(char[] str, int L, int R) { + if (L == R) { + return 1; + } + if (L == R - 1) { + return str[L] == str[R] ? 2 : 1; + } + int p1 = f(str, L + 1, R - 1); + int p2 = f(str, L, R - 1); + int p3 = f(str, L + 1, R); + int p4 = str[L] != str[R] ? 0 : (2 + f(str, L + 1, R - 1)); + return Math.max(Math.max(p1, p2), Math.max(p3, p4)); + } + + public static int lpsl2(String s) { + if (s == null || s.length() == 0) { + return 0; + } + char[] str = s.toCharArray(); + int N = str.length; + int[][] dp = new int[N][N]; + dp[N - 1][N - 1] = 1; + for (int i = 0; i < N - 1; i++) { + dp[i][i] = 1; + dp[i][i + 1] = str[i] == str[i + 1] ? 2 : 1; + } + for (int L = N - 3; L >= 0; L--) { + for (int R = L + 2; R < N; R++) { + dp[L][R] = Math.max(dp[L][R - 1], dp[L + 1][R]); + if (str[L] == str[R]) { + dp[L][R] = Math.max(dp[L][R], 2 + dp[L + 1][R - 1]); + } + } + } + return dp[0][N - 1]; + } + + public static int longestPalindromeSubseq1(String s) { + if (s == null || s.length() == 0) { + return 0; + } + if (s.length() == 1) { + return 1; + } + char[] str = s.toCharArray(); + char[] reverse = reverse(str); + return longestCommonSubsequence(str, reverse); + } + + public static char[] reverse(char[] str) { + int N = str.length; + char[] reverse = new char[str.length]; + for (int i = 0; i < str.length; i++) { + reverse[--N] = str[i]; + } + return reverse; + } + + public static int longestCommonSubsequence(char[] str1, char[] str2) { + int N = str1.length; + int M = str2.length; + int[][] dp = new int[N][M]; + dp[0][0] = str1[0] == str2[0] ? 1 : 0; + for (int i = 1; i < N; i++) { + dp[i][0] = str1[i] == str2[0] ? 1 : dp[i - 1][0]; + } + for (int j = 1; j < M; j++) { + dp[0][j] = str1[0] == str2[j] ? 1 : dp[0][j - 1]; + } + for (int i = 1; i < N; i++) { + for (int j = 1; j < M; j++) { + dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]); + if (str1[i] == str2[j]) { + dp[i][j] = Math.max(dp[i][j], dp[i - 1][j - 1] + 1); + } + } + } + return dp[N - 1][M - 1]; + } + + public static int longestPalindromeSubseq2(String s) { + if (s == null || s.length() == 0) { + return 0; + } + if (s.length() == 1) { + return 1; + } + char[] str = s.toCharArray(); + int N = str.length; + int[][] dp = new int[N][N]; + dp[N - 1][N - 1] = 1; + for (int i = 0; i < N - 1; i++) { + dp[i][i] = 1; + dp[i][i + 1] = str[i] == str[i + 1] ? 2 : 1; + } + for (int i = N - 3; i >= 0; i--) { + for (int j = i + 2; j < N; j++) { + dp[i][j] = Math.max(dp[i][j - 1], dp[i + 1][j]); + if (str[i] == str[j]) { + dp[i][j] = Math.max(dp[i][j], dp[i + 1][j - 1] + 2); + } + } + } + return dp[0][N - 1]; + } + +} diff --git a/MCA算法突击课/第01期_test/Code01_SumNoPositiveMinCost.java b/MCA算法突击课/第01期_test/Code01_SumNoPositiveMinCost.java new file mode 100644 index 0000000..423f533 --- /dev/null +++ b/MCA算法突击课/第01期_test/Code01_SumNoPositiveMinCost.java @@ -0,0 +1,197 @@ +package 第01期_test; + +import java.util.Arrays; + +// 来自微软面试 +// 给定一个正数数组arr长度为n、正数x、正数y +// 你的目标是让arr整体的累加和<=0 +// 你可以对数组中的数num执行以下三种操作中的一种,且每个数最多能执行一次操作 : +// 1)不变 +// 2)可以选择让num变成0,承担x的代价 +// 3)可以选择让num变成-num,承担y的代价 +// 返回你达到目标的最小代价 +// 数据规模 : 面试时面试官没有说数据规模 +public class Code01_SumNoPositiveMinCost { + + // 动态规划 + public static int minOpStep1(int[] arr, int x, int y) { + int sum = 0; + for (int num : arr) { + sum += num; + } + return process1(arr, x, y, 0, sum); + } + + // arr[i...]自由选择,每个位置的数可以执行三种操作中的一种! + // 执行变0的操作,x操作,代价 -> x + // 执行变相反数的操作,y操作,代价 -> y + // 还剩下sum这么多累加和,需要去搞定! + // 返回搞定了sum,最低代价是多少? + public static int process1(int[] arr, int x, int y, int i, int sum) { + if (sum <= 0) { + return 0; + } + // sum > 0 没搞定 + if (i == arr.length) { + return Integer.MAX_VALUE; + } + // 第一选择,什么也不干! + int p1 = process1(arr, x, y, i + 1, sum); + // 第二选择,执行x的操作,变0 x + 后续 + int p2 = Integer.MAX_VALUE; + int next2 = process1(arr, x, y, i + 1, sum - arr[i]); + if (next2 != Integer.MAX_VALUE) { + p2 = x + next2; + } + // 第三选择,执行y的操作,变相反数 x + 后续 7 -7 -14 + int p3 = Integer.MAX_VALUE; + int next3 = process1(arr, x, y, i + 1, sum - (arr[i] << 1)); + if (next3 != Integer.MAX_VALUE) { + p3 = y + next3; + } + return Math.min(p1, Math.min(p2, p3)); + } + + // 贪心(最优解) + public static int minOpStep2(int[] arr, int x, int y) { + Arrays.sort(arr); // 小 -> 大 + int n = arr.length; + for (int l = 0, r = n - 1; l <= r; l++, r--) { + int tmp = arr[l]; + arr[l] = arr[r]; + arr[r] = tmp; + } + // arr 大 -> 小 + if (x >= y) { // 没有任何必要执行x操作 + int sum = 0; + for (int num : arr) { + sum += num; + } + int cost = 0; + for (int i = 0; i < n && sum > 0; i++) { + sum -= arr[i] << 1; + cost += y; + } + return cost; + } else { + for (int i = n - 2; i >= 0; i--) { + arr[i] += arr[i + 1]; + } + int benefit = 0; + // 注意,可以不二分,用不回退的方式! + // 执行Y操作的数,有0个的时候 + int left = mostLeft(arr, 0, benefit); + int cost = left * x; + for (int i = 0; i < n - 1; i++) { + // 0..i 这些数,都执行Y + benefit += arr[i] - arr[i + 1]; + left = mostLeft(arr, i + 1, benefit); + cost = Math.min(cost, (i + 1) * y + (left - i - 1) * x); + } + return cost; + } + } + + // arr是后缀和数组, arr[l...]中找到值<=v的最左位置 + public static int mostLeft(int[] arr, int l, int v) { + int r = arr.length - 1; + int m = 0; + int ans = arr.length; + while (l <= r) { + m = (l + r) / 2; + if (arr[m] <= v) { + ans = m; + r = m - 1; + } else { + l = m + 1; + } + } + return ans; + } + + // 不回退 + public static int minOpStep3(int[] arr, int x, int y) { + // 系统排序,小 -> 大 + Arrays.sort(arr); + int n = arr.length; + // 如何变成 大 -> 小 + for (int l = 0, r = n - 1; l <= r; l++, r--) { + int tmp = arr[l]; + arr[l] = arr[r]; + arr[r] = tmp; + } + if (x >= y) { + int sum = 0; + for (int num : arr) { + sum += num; + } + int cost = 0; + for (int i = 0; i < n && sum > 0; i++) { + sum -= arr[i] << 1; + cost += y; + } + return cost; + } else { + // 0个数执行Y + int benefit = 0; + // 全部的数都需要执行x,才能让累加和<=0 + int cost = arr.length * x; + int holdSum = 0; + for (int yRight = 0, holdLeft = n; yRight < holdLeft - 1; yRight++) { + benefit += arr[yRight]; + while (holdLeft - 1 > yRight && holdSum + arr[holdLeft - 1] <= benefit) { + holdSum += arr[holdLeft - 1]; + holdLeft--; + } + // 0...yRight x holdLeft.... + cost = Math.min(cost, (yRight + 1) * y + (holdLeft - yRight - 1) * x); + } + return cost; + } + } + + // 为了测试 + public static int[] randomArray(int len, int v) { + int[] arr = new int[len]; + for (int i = 0; i < len; i++) { + arr[i] = (int) (Math.random() * v) + 1; + } + return arr; + } + + // 为了测试 + public static int[] copyArray(int[] arr) { + int[] ans = new int[arr.length]; + for (int i = 0; i < arr.length; i++) { + ans[i] = arr[i]; + } + return ans; + } + + // 为了测试 + public static void main(String[] args) { + int n = 12; + int v = 20; + int c = 10; + int testTime = 10000; + System.out.println("测试开始"); + for (int i = 0; i < testTime; i++) { + int len = (int) (Math.random() * n); + int[] arr = randomArray(len, v); + int[] arr1 = copyArray(arr); + int[] arr2 = copyArray(arr); + int[] arr3 = copyArray(arr); + int x = (int) (Math.random() * c); + int y = (int) (Math.random() * c); + int ans1 = minOpStep1(arr1, x, y); + int ans2 = minOpStep2(arr2, x, y); + int ans3 = minOpStep3(arr3, x, y); + if (ans1 != ans2 || ans1 != ans3) { + System.out.println("出错了!"); + } + } + System.out.println("测试结束"); + + } + +} diff --git a/MCA算法突击课/第01期_test/Code02_AllLessNumSubArray.java b/MCA算法突击课/第01期_test/Code02_AllLessNumSubArray.java new file mode 100644 index 0000000..469c687 --- /dev/null +++ b/MCA算法突击课/第01期_test/Code02_AllLessNumSubArray.java @@ -0,0 +1,109 @@ +package 第01期_test; + +import java.util.LinkedList; + +public class Code02_AllLessNumSubArray { + + // 暴力的对数器方法 + public static int right(int[] arr, int sum) { + if (arr == null || arr.length == 0 || sum < 0) { + return 0; + } + int N = arr.length; + int count = 0; + for (int L = 0; L < N; L++) { + for (int R = L; R < N; R++) { + int max = arr[L]; + int min = arr[L]; + for (int i = L + 1; i <= R; i++) { + max = Math.max(max, arr[i]); + min = Math.min(min, arr[i]); + } + if (max - min <= sum) { + count++; + } + } + } + return count; + } + + public static int num(int[] arr, int sum) { + if (arr == null || arr.length == 0 || sum < 0) { + return 0; + } + int N = arr.length; + int count = 0; + LinkedList maxWindow = new LinkedList<>(); + LinkedList minWindow = new LinkedList<>(); + int R = 0; + for (int L = 0; L < N; L++) { + while (R < N) { + while (!maxWindow.isEmpty() && arr[maxWindow.peekLast()] <= arr[R]) { + maxWindow.pollLast(); + } + maxWindow.addLast(R); + while (!minWindow.isEmpty() && arr[minWindow.peekLast()] >= arr[R]) { + minWindow.pollLast(); + } + minWindow.addLast(R); + if (arr[maxWindow.peekFirst()] - arr[minWindow.peekFirst()] > sum) { + break; + } else { + R++; + } + } + count += R - L; + if (maxWindow.peekFirst() == L) { + maxWindow.pollFirst(); + } + if (minWindow.peekFirst() == L) { + minWindow.pollFirst(); + } + } + return count; + } + + // for test + public static int[] generateRandomArray(int maxLen, int maxValue) { + int len = (int) (Math.random() * (maxLen + 1)); + int[] arr = new int[len]; + for (int i = 0; i < len; i++) { + arr[i] = (int) (Math.random() * (maxValue + 1)) - (int) (Math.random() * (maxValue + 1)); + } + return arr; + } + + // for test + public static void printArray(int[] arr) { + if (arr != null) { + for (int i = 0; i < arr.length; i++) { + System.out.print(arr[i] + " "); + } + System.out.println(); + } + } + + public static void main(String[] args) { + int maxLen = 100; + int maxValue = 200; + int testTime = 100000; + System.out.println("测试开始"); + for (int i = 0; i < testTime; i++) { + int[] arr = generateRandomArray(maxLen, maxValue); + int sum = (int) (Math.random() * (maxValue + 1)); + int ans1 = right(arr, sum); + int ans2 = num(arr, sum); + if (ans1 != ans2) { + System.out.println("Oops!"); + printArray(arr); + System.out.println(sum); + System.out.println(ans1); + System.out.println(ans2); + break; + } + } + System.out.println("测试结束"); + + } + +} diff --git a/MCA算法突击课/第01期_test/Code02_BestTimeToBuyAndSellStockII.java b/MCA算法突击课/第01期_test/Code02_BestTimeToBuyAndSellStockII.java new file mode 100644 index 0000000..f4d087b --- /dev/null +++ b/MCA算法突击课/第01期_test/Code02_BestTimeToBuyAndSellStockII.java @@ -0,0 +1,17 @@ +package 第01期_test; + +//leetcode 122 +public class Code02_BestTimeToBuyAndSellStockII { + + public static int maxProfit(int[] prices) { + if (prices == null || prices.length == 0) { + return 0; + } + int ans = 0; + for (int i = 1; i < prices.length; i++) { + ans += Math.max(prices[i] - prices[i-1], 0); + } + return ans; + } + +} diff --git a/MCA算法突击课/第01期_test/Code02_CoinsWayEveryPaperDifferent.java b/MCA算法突击课/第01期_test/Code02_CoinsWayEveryPaperDifferent.java new file mode 100644 index 0000000..498d595 --- /dev/null +++ b/MCA算法突击课/第01期_test/Code02_CoinsWayEveryPaperDifferent.java @@ -0,0 +1,77 @@ +package 第01期_test; + +public class Code02_CoinsWayEveryPaperDifferent { + + public static int coinWays(int[] arr, int aim) { + return process(arr, 0, aim); + } + + // arr[index....] 组成正好rest这么多的钱,有几种方法 + public static int process(int[] arr, int index, int rest) { + if (rest < 0) { + return 0; + } + if (index == arr.length) { // 没钱了! + return rest == 0 ? 1 : 0; + } else { + return process(arr, index + 1, rest) + process(arr, index + 1, rest - arr[index]); + } + } + + public static int dp(int[] arr, int aim) { + if (aim == 0) { + return 1; + } + int N = arr.length; + int[][] dp = new int[N + 1][aim + 1]; + dp[N][0] = 1; + for (int index = N - 1; index >= 0; index--) { + for (int rest = 0; rest <= aim; rest++) { + dp[index][rest] = dp[index + 1][rest] + (rest - arr[index] >= 0 ? dp[index + 1][rest - arr[index]] : 0); + } + } + return dp[0][aim]; + } + + // 为了测试 + public static int[] randomArray(int maxLen, int maxValue) { + int N = (int) (Math.random() * maxLen); + int[] arr = new int[N]; + for (int i = 0; i < N; i++) { + arr[i] = (int) (Math.random() * maxValue) + 1; + } + return arr; + } + + // 为了测试 + public static void printArray(int[] arr) { + for (int i = 0; i < arr.length; i++) { + System.out.print(arr[i] + " "); + } + System.out.println(); + } + + // 为了测试 + public static void main(String[] args) { + int maxLen = 20; + int maxValue = 30; + int testTime = 1000000; + System.out.println("测试开始"); + for (int i = 0; i < testTime; i++) { + int[] arr = randomArray(maxLen, maxValue); + int aim = (int) (Math.random() * maxValue); + int ans1 = coinWays(arr, aim); + int ans2 = dp(arr, aim); + if (ans1 != ans2) { + System.out.println("Oops!"); + printArray(arr); + System.out.println(aim); + System.out.println(ans1); + System.out.println(ans2); + break; + } + } + System.out.println("测试结束"); + } + +} diff --git a/MCA算法突击课/第01期_test/Code02_ContainerWithMostWater.java b/MCA算法突击课/第01期_test/Code02_ContainerWithMostWater.java new file mode 100644 index 0000000..b6414af --- /dev/null +++ b/MCA算法突击课/第01期_test/Code02_ContainerWithMostWater.java @@ -0,0 +1,32 @@ +package 第01期_test; + +// 本题测试链接 : https://leetcode.com/problems/container-with-most-water/ +public class Code02_ContainerWithMostWater { + + public static int maxArea1(int[] h) { + int max = 0; + int N = h.length; + for (int i = 0; i < N; i++) { // h[i] + for (int j = i + 1; j < N; j++) { // h[j] + max = Math.max(max, Math.min(h[i], h[j]) * (j - i)); + } + } + return max; + } + + public static int maxArea2(int[] h) { + int max = 0; + int l = 0; + int r = h.length - 1; + while (l < r) { + max = Math.max(max, Math.min(h[l], h[r]) * (r - l)); + if (h[l] > h[r]) { + r--; + } else { + l++; + } + } + return max; + } + +} diff --git a/MCA算法突击课/第01期_test/Code02_ConvertToLetterString.java b/MCA算法突击课/第01期_test/Code02_ConvertToLetterString.java new file mode 100644 index 0000000..71a5626 --- /dev/null +++ b/MCA算法突击课/第01期_test/Code02_ConvertToLetterString.java @@ -0,0 +1,123 @@ +package 第01期_test; + +public class Code02_ConvertToLetterString { + + // str只含有数字字符0~9 + // 返回多少种转化方案 + public static int number(String str) { + if (str == null || str.length() == 0) { + return 0; + } + return process(str.toCharArray(), 0); + } + + // str[0..i-1]转化无需过问 + // str[i.....]去转化,返回有多少种转化方法 + public static int process(char[] str, int i) { + if (i == str.length) { + return 1; + } + // i没到最后,说明有字符 + if (str[i] == '0') { // 之前的决定有问题 + return 0; + } + // str[i] != '0' + // 可能性一,i单转 + int ways = process(str, i + 1); + if (i + 1 < str.length && (str[i] - '0') * 10 + str[i + 1] - '0' < 27) { + ways += process(str, i + 2); + } + return ways; + } + + // 从右往左的动态规划 + // 就是上面方法的动态规划版本 + // dp[i]表示:str[i...]有多少种转化方式 + public static int dp1(String s) { + if (s == null || s.length() == 0) { + return 0; + } + char[] str = s.toCharArray(); + int N = str.length; + int[] dp = new int[N + 1]; + dp[N] = 1; + for (int i = N - 1; i >= 0; i--) { + if (str[i] != '0') { + int ways = dp[i + 1]; + if (i + 1 < str.length && (str[i] - '0') * 10 + str[i + 1] - '0' < 27) { + ways += dp[i + 2]; + } + dp[i] = ways; + } + } + return dp[0]; + } + + // 从左往右的动态规划 + // dp[i]表示:str[0...i]有多少种转化方式 + public static int dp2(String s) { + if (s == null || s.length() == 0) { + return 0; + } + char[] str = s.toCharArray(); + int N = str.length; + if (str[0] == '0') { + return 0; + } + int[] dp = new int[N]; + dp[0] = 1; + for (int i = 1; i < N; i++) { + if (str[i] == '0') { + // 如果此时str[i]=='0',那么他是一定要拉前一个字符(i-1的字符)一起拼的, + // 那么就要求前一个字符,不能也是‘0’,否则拼不了。 + // 前一个字符不是‘0’就够了嘛?不够,还得要求拼完了要么是10,要么是20,如果更大的话,拼不了。 + // 这就够了嘛?还不够,你们拼完了,还得要求str[0...i-2]真的可以被分解! + // 如果str[0...i-2]都不存在分解方案,那i和i-1拼成了也不行,因为之前的搞定不了。 + if (str[i - 1] == '0' || str[i - 1] > '2' || (i - 2 >= 0 && dp[i - 2] == 0)) { + return 0; + } else { + dp[i] = i - 2 >= 0 ? dp[i - 2] : 1; + } + } else { + dp[i] = dp[i - 1]; + if (str[i - 1] != '0' && (str[i - 1] - '0') * 10 + str[i] - '0' <= 26) { + dp[i] += i - 2 >= 0 ? dp[i - 2] : 1; + } + } + } + return dp[N - 1]; + } + + // 为了测试 + public static String randomString(int len) { + char[] str = new char[len]; + for (int i = 0; i < len; i++) { + str[i] = (char) ((int) (Math.random() * 10) + '0'); + } + return String.valueOf(str); + } + + // 为了测试 + public static void main(String[] args) { + int N = 30; + int testTime = 1000000; + System.out.println("测试开始"); + for (int i = 0; i < testTime; i++) { + int len = (int) (Math.random() * N); + String s = randomString(len); + int ans0 = number(s); + int ans1 = dp1(s); + int ans2 = dp2(s); + if (ans0 != ans1 || ans0 != ans2) { + System.out.println(s); + System.out.println(ans0); + System.out.println(ans1); + System.out.println(ans2); + System.out.println("Oops!"); + break; + } + } + System.out.println("测试结束"); + } + +} diff --git a/MCA算法突击课/第01期_test/Code02_HorseJump.java b/MCA算法突击课/第01期_test/Code02_HorseJump.java new file mode 100644 index 0000000..f907434 --- /dev/null +++ b/MCA算法突击课/第01期_test/Code02_HorseJump.java @@ -0,0 +1,109 @@ +package 第01期_test; + +public class Code02_HorseJump { + + // 当前来到的位置是(x,y) + // 还剩下rest步需要跳 + // 跳完rest步,正好跳到a,b的方法数是多少? + // 10 * 9 + public static int jump(int a, int b, int k) { + return process(0, 0, k, a, b); + } + + public static int process(int x, int y, int rest, int a, int b) { + if (x < 0 || x > 9 || y < 0 || y > 8) { + return 0; + } + if (rest == 0) { + return (x == a && y == b) ? 1 : 0; + } + int ways = process(x + 2, y + 1, rest - 1, a, b); + ways += process(x + 1, y + 2, rest - 1, a, b); + ways += process(x - 1, y + 2, rest - 1, a, b); + ways += process(x - 2, y + 1, rest - 1, a, b); + ways += process(x - 2, y - 1, rest - 1, a, b); + ways += process(x - 1, y - 2, rest - 1, a, b); + ways += process(x + 1, y - 2, rest - 1, a, b); + ways += process(x + 2, y - 1, rest - 1, a, b); + return ways; + } + + public static int dp(int a, int b, int k) { + int[][][] dp = new int[10][9][k + 1]; + dp[a][b][0] = 1; + for (int rest = 1; rest <= k; rest++) { + for (int x = 0; x < 10; x++) { + for (int y = 0; y < 9; y++) { + int ways = pick(dp, x + 2, y + 1, rest - 1); + ways += pick(dp, x + 1, y + 2, rest - 1); + ways += pick(dp, x - 1, y + 2, rest - 1); + ways += pick(dp, x - 2, y + 1, rest - 1); + ways += pick(dp, x - 2, y - 1, rest - 1); + ways += pick(dp, x - 1, y - 2, rest - 1); + ways += pick(dp, x + 1, y - 2, rest - 1); + ways += pick(dp, x + 2, y - 1, rest - 1); + dp[x][y][rest] = ways; + } + } + } + return dp[0][0][k]; + } + + public static int pick(int[][][] dp, int x, int y, int rest) { + if (x < 0 || x > 9 || y < 0 || y > 8) { + return 0; + } + return dp[x][y][rest]; + } + + public static int ways(int a, int b, int step) { + return f(0, 0, step, a, b); + } + + public static int f(int i, int j, int step, int a, int b) { + if (i < 0 || i > 9 || j < 0 || j > 8) { + return 0; + } + if (step == 0) { + return (i == a && j == b) ? 1 : 0; + } + return f(i - 2, j + 1, step - 1, a, b) + f(i - 1, j + 2, step - 1, a, b) + f(i + 1, j + 2, step - 1, a, b) + + f(i + 2, j + 1, step - 1, a, b) + f(i + 2, j - 1, step - 1, a, b) + f(i + 1, j - 2, step - 1, a, b) + + f(i - 1, j - 2, step - 1, a, b) + f(i - 2, j - 1, step - 1, a, b); + + } + + public static int waysdp(int a, int b, int s) { + int[][][] dp = new int[10][9][s + 1]; + dp[a][b][0] = 1; + for (int step = 1; step <= s; step++) { // 按层来 + for (int i = 0; i < 10; i++) { + for (int j = 0; j < 9; j++) { + dp[i][j][step] = getValue(dp, i - 2, j + 1, step - 1) + getValue(dp, i - 1, j + 2, step - 1) + + getValue(dp, i + 1, j + 2, step - 1) + getValue(dp, i + 2, j + 1, step - 1) + + getValue(dp, i + 2, j - 1, step - 1) + getValue(dp, i + 1, j - 2, step - 1) + + getValue(dp, i - 1, j - 2, step - 1) + getValue(dp, i - 2, j - 1, step - 1); + } + } + } + return dp[0][0][s]; + } + + // 在dp表中,得到dp[i][j][step]的值,但如果(i,j)位置越界的话,返回0; + public static int getValue(int[][][] dp, int i, int j, int step) { + if (i < 0 || i > 9 || j < 0 || j > 8) { + return 0; + } + return dp[i][j][step]; + } + + public static void main(String[] args) { + int x = 7; + int y = 7; + int step = 10; + System.out.println(ways(x, y, step)); + System.out.println(dp(x, y, step)); + + System.out.println(jump(x, y, step)); + } +} diff --git a/MCA算法突击课/第01期_test/Code02_LessMoneySplitGold.java b/MCA算法突击课/第01期_test/Code02_LessMoneySplitGold.java new file mode 100644 index 0000000..ccd13cd --- /dev/null +++ b/MCA算法突击课/第01期_test/Code02_LessMoneySplitGold.java @@ -0,0 +1,79 @@ +package 第01期_test; + +import java.util.PriorityQueue; + +public class Code02_LessMoneySplitGold { + + // 纯暴力! + public static int lessMoney1(int[] arr) { + if (arr == null || arr.length == 0) { + return 0; + } + return process(arr, 0); + } + + // 等待合并的数都在arr里,pre之前的合并行为产生了多少总代价 + // arr中只剩一个数字的时候,停止合并,返回最小的总代价 + public static int process(int[] arr, int pre) { + if (arr.length == 1) { + return pre; + } + int ans = Integer.MAX_VALUE; + for (int i = 0; i < arr.length; i++) { + for (int j = i + 1; j < arr.length; j++) { + ans = Math.min(ans, process(copyAndMergeTwo(arr, i, j), pre + arr[i] + arr[j])); + } + } + return ans; + } + + public static int[] copyAndMergeTwo(int[] arr, int i, int j) { + int[] ans = new int[arr.length - 1]; + int ansi = 0; + for (int arri = 0; arri < arr.length; arri++) { + if (arri != i && arri != j) { + ans[ansi++] = arr[arri]; + } + } + ans[ansi] = arr[i] + arr[j]; + return ans; + } + + public static int lessMoney2(int[] arr) { + PriorityQueue pQ = new PriorityQueue<>(); + for (int i = 0; i < arr.length; i++) { + pQ.add(arr[i]); + } + int sum = 0; + int cur = 0; + while (pQ.size() > 1) { + cur = pQ.poll() + pQ.poll(); + sum += cur; + pQ.add(cur); + } + return sum; + } + + // for test + public static int[] generateRandomArray(int maxSize, int maxValue) { + int[] arr = new int[(int) ((maxSize + 1) * Math.random())]; + for (int i = 0; i < arr.length; i++) { + arr[i] = (int) (Math.random() * (maxValue + 1)); + } + return arr; + } + + public static void main(String[] args) { + int testTime = 100000; + int maxSize = 6; + int maxValue = 1000; + for (int i = 0; i < testTime; i++) { + int[] arr = generateRandomArray(maxSize, maxValue); + if (lessMoney1(arr) != lessMoney2(arr)) { + System.out.println("Oops!"); + } + } + System.out.println("finish!"); + } + +} diff --git a/MCA算法突击课/第01期_test/Code02_MinCoinsNoLimit.java b/MCA算法突击课/第01期_test/Code02_MinCoinsNoLimit.java new file mode 100644 index 0000000..bfc6bb6 --- /dev/null +++ b/MCA算法突击课/第01期_test/Code02_MinCoinsNoLimit.java @@ -0,0 +1,121 @@ +package 第01期_test; + +public class Code02_MinCoinsNoLimit { + + public static int minCoins(int[] arr, int aim) { + return process(arr, 0, aim); + } + + // arr[index...]面值,每种面值张数自由选择, + // 搞出rest正好这么多钱,返回最小张数 + // 拿Integer.MAX_VALUE标记怎么都搞定不了 + public static int process(int[] arr, int index, int rest) { + if (index == arr.length) { + return rest == 0 ? 0 : Integer.MAX_VALUE; + } else { + int ans = Integer.MAX_VALUE; + for (int zhang = 0; zhang * arr[index] <= rest; zhang++) { + int next = process(arr, index + 1, rest - zhang * arr[index]); + if (next != Integer.MAX_VALUE) { + ans = Math.min(ans, zhang + next); + } + } + return ans; + } + } + + public static int dp1(int[] arr, int aim) { + if (aim == 0) { + return 0; + } + int N = arr.length; + int[][] dp = new int[N + 1][aim + 1]; + dp[N][0] = 0; + for (int j = 1; j <= aim; j++) { + dp[N][j] = Integer.MAX_VALUE; + } + for (int index = N - 1; index >= 0; index--) { + for (int rest = 0; rest <= aim; rest++) { + int ans = Integer.MAX_VALUE; + for (int zhang = 0; zhang * arr[index] <= rest; zhang++) { + int next = dp[index + 1][rest - zhang * arr[index]]; + if (next != Integer.MAX_VALUE) { + ans = Math.min(ans, zhang + next); + } + } + dp[index][rest] = ans; + } + } + return dp[0][aim]; + } + + public static int dp2(int[] arr, int aim) { + if (aim == 0) { + return 0; + } + int N = arr.length; + int[][] dp = new int[N + 1][aim + 1]; + dp[N][0] = 0; + for (int j = 1; j <= aim; j++) { + dp[N][j] = Integer.MAX_VALUE; + } + for (int index = N - 1; index >= 0; index--) { + for (int rest = 0; rest <= aim; rest++) { + dp[index][rest] = dp[index + 1][rest]; + if (rest - arr[index] >= 0 + && dp[index][rest - arr[index]] != Integer.MAX_VALUE) { + dp[index][rest] = Math.min(dp[index][rest], dp[index][rest - arr[index]] + 1); + } + } + } + return dp[0][aim]; + } + + // 为了测试 + public static int[] randomArray(int maxLen, int maxValue) { + int N = (int) (Math.random() * maxLen); + int[] arr = new int[N]; + boolean[] has = new boolean[maxValue + 1]; + for (int i = 0; i < N; i++) { + do { + arr[i] = (int) (Math.random() * maxValue) + 1; + } while (has[arr[i]]); + has[arr[i]] = true; + } + return arr; + } + + // 为了测试 + public static void printArray(int[] arr) { + for (int i = 0; i < arr.length; i++) { + System.out.print(arr[i] + " "); + } + System.out.println(); + } + + // 为了测试 + public static void main(String[] args) { + int maxLen = 20; + int maxValue = 30; + int testTime = 300000; + System.out.println("功能测试开始"); + for (int i = 0; i < testTime; i++) { + int N = (int) (Math.random() * maxLen); + int[] arr = randomArray(N, maxValue); + int aim = (int) (Math.random() * maxValue); + int ans1 = minCoins(arr, aim); + int ans2 = dp1(arr, aim); + int ans3 = dp2(arr, aim); + if (ans1 != ans2 || ans1 != ans3) { + System.out.println("Oops!"); + printArray(arr); + System.out.println(aim); + System.out.println(ans1); + System.out.println(ans2); + break; + } + } + System.out.println("功能测试结束"); + } + +} diff --git a/MCA算法突击课/第01期_test/Code02_SubArrayMaxSum.java b/MCA算法突击课/第01期_test/Code02_SubArrayMaxSum.java new file mode 100644 index 0000000..882fded --- /dev/null +++ b/MCA算法突击课/第01期_test/Code02_SubArrayMaxSum.java @@ -0,0 +1,35 @@ +package 第01期_test; + +// 本题测试链接 : https://leetcode.com/problems/maximum-subarray/ +public class Code02_SubArrayMaxSum { + + public static int maxSubArray(int[] arr) { + if (arr == null || arr.length == 0) { + return 0; + } + int max = Integer.MIN_VALUE; + int cur = 0; + for (int i = 0; i < arr.length; i++) { + cur += arr[i]; + max = Math.max(max, cur); + cur = cur < 0 ? 0 : cur; + } + return max; + } + + public static int maxSubArray2(int[] arr) { + if (arr == null || arr.length == 0) { + return 0; + } + // 上一步,dp的值 + // dp[0] + int pre = arr[0]; + int max = arr[0]; + for (int i = 1; i < arr.length; i++) { + pre = Math.max(arr[i], arr[i] + pre); + max = Math.max(max, pre); + } + return max; + } + +} diff --git a/MCA算法突击课/第01期_test/Code03_BestTimeToBuyAndSellStockIII.java b/MCA算法突击课/第01期_test/Code03_BestTimeToBuyAndSellStockIII.java new file mode 100644 index 0000000..838b695 --- /dev/null +++ b/MCA算法突击课/第01期_test/Code03_BestTimeToBuyAndSellStockIII.java @@ -0,0 +1,23 @@ +package 第01期_test; + +//leetcode 123 +public class Code03_BestTimeToBuyAndSellStockIII { + + public static int maxProfit(int[] prices) { + if (prices == null || prices.length < 2) { + return 0; + } + int ans = 0; + int doneOnceMinusBuyMax = -prices[0]; + int doneOnceMax = 0; + int min = prices[0]; + for (int i = 1; i < prices.length; i++) { + min = Math.min(min, prices[i]); + ans = Math.max(ans, doneOnceMinusBuyMax + prices[i]); + doneOnceMax = Math.max(doneOnceMax, prices[i] - min); + doneOnceMinusBuyMax = Math.max(doneOnceMinusBuyMax, doneOnceMax - prices[i]); + } + return ans; + } + +} diff --git a/MCA算法突击课/第01期_test/Code03_CoinsWayNoLimit.java b/MCA算法突击课/第01期_test/Code03_CoinsWayNoLimit.java new file mode 100644 index 0000000..b0fcdb0 --- /dev/null +++ b/MCA算法突击课/第01期_test/Code03_CoinsWayNoLimit.java @@ -0,0 +1,108 @@ +package 第01期_test; + +public class Code03_CoinsWayNoLimit { + + public static int coinsWay(int[] arr, int aim) { + if (arr == null || arr.length == 0 || aim < 0) { + return 0; + } + return process(arr, 0, aim); + } + + // arr[index....] 所有的面值,每一个面值都可以任意选择张数,组成正好rest这么多钱,方法数多少? + public static int process(int[] arr, int index, int rest) { + if (index == arr.length) { // 没钱了 + return rest == 0 ? 1 : 0; + } + int ways = 0; + for (int zhang = 0; zhang * arr[index] <= rest; zhang++) { + ways += process(arr, index + 1, rest - (zhang * arr[index])); + } + return ways; + } + + public static int dp1(int[] arr, int aim) { + if (arr == null || arr.length == 0 || aim < 0) { + return 0; + } + int N = arr.length; + int[][] dp = new int[N + 1][aim + 1]; + dp[N][0] = 1; + for (int index = N - 1; index >= 0; index--) { + for (int rest = 0; rest <= aim; rest++) { + int ways = 0; + for (int zhang = 0; zhang * arr[index] <= rest; zhang++) { + ways += dp[index + 1][rest - (zhang * arr[index])]; + } + dp[index][rest] = ways; + } + } + return dp[0][aim]; + } + + public static int dp2(int[] arr, int aim) { + if (arr == null || arr.length == 0 || aim < 0) { + return 0; + } + int N = arr.length; + int[][] dp = new int[N + 1][aim + 1]; + dp[N][0] = 1; + for (int index = N - 1; index >= 0; index--) { + for (int rest = 0; rest <= aim; rest++) { + dp[index][rest] = dp[index + 1][rest]; + if (rest - arr[index] >= 0) { + dp[index][rest] += dp[index][rest - arr[index]]; + } + } + } + return dp[0][aim]; + } + + // 为了测试 + public static int[] randomArray(int maxLen, int maxValue) { + int N = (int) (Math.random() * maxLen); + int[] arr = new int[N]; + boolean[] has = new boolean[maxValue + 1]; + for (int i = 0; i < N; i++) { + do { + arr[i] = (int) (Math.random() * maxValue) + 1; + } while (has[arr[i]]); + has[arr[i]] = true; + } + return arr; + } + + // 为了测试 + public static void printArray(int[] arr) { + for (int i = 0; i < arr.length; i++) { + System.out.print(arr[i] + " "); + } + System.out.println(); + } + + // 为了测试 + public static void main(String[] args) { + int maxLen = 10; + int maxValue = 30; + int testTime = 1000000; + System.out.println("测试开始"); + for (int i = 0; i < testTime; i++) { + int[] arr = randomArray(maxLen, maxValue); + int aim = (int) (Math.random() * maxValue); + int ans1 = coinsWay(arr, aim); + int ans2 = dp1(arr, aim); + int ans3 = dp2(arr, aim); + if (ans1 != ans2 || ans1 != ans3) { + System.out.println("Oops!"); + printArray(arr); + System.out.println(aim); + System.out.println(ans1); + System.out.println(ans2); + System.out.println(ans3); + break; + } + } + System.out.println("测试结束"); + } + +} diff --git a/MCA算法突击课/第01期_test/Code03_SplitNumber.java b/MCA算法突击课/第01期_test/Code03_SplitNumber.java new file mode 100644 index 0000000..e3debae --- /dev/null +++ b/MCA算法突击课/第01期_test/Code03_SplitNumber.java @@ -0,0 +1,85 @@ +package 第01期_test; + +public class Code03_SplitNumber { + + // n为正数 + public static int ways(int n) { + if (n < 0) { + return 0; + } + if (n == 1) { + return 1; + } + return process(1, n); + } + + // 上一个拆出来的数是pre + // 还剩rest需要去拆 + // 返回拆解的方法数 + public static int process(int pre, int rest) { + if (rest == 0) { + return 1; + } + if (pre > rest) { + return 0; + } + int ways = 0; + for (int first = pre; first <= rest; first++) { + ways += process(first, rest - first); + } + return ways; + } + + public static int dp1(int n) { + if (n < 0) { + return 0; + } + if (n == 1) { + return 1; + } + int[][] dp = new int[n + 1][n + 1]; + for (int pre = 1; pre <= n; pre++) { + dp[pre][0] = 1; + dp[pre][pre] = 1; + } + for (int pre = n - 1; pre >= 1; pre--) { + for (int rest = pre + 1; rest <= n; rest++) { + int ways = 0; + for (int first = pre; first <= rest; first++) { + ways += dp[first][rest - first]; + } + dp[pre][rest] = ways; + } + } + return dp[1][n]; + } + + public static int dp2(int n) { + if (n < 0) { + return 0; + } + if (n == 1) { + return 1; + } + int[][] dp = new int[n + 1][n + 1]; + for (int pre = 1; pre <= n; pre++) { + dp[pre][0] = 1; + dp[pre][pre] = 1; + } + for (int pre = n - 1; pre >= 1; pre--) { + for (int rest = pre + 1; rest <= n; rest++) { + dp[pre][rest] = dp[pre + 1][rest]; + dp[pre][rest] += dp[pre][rest - pre]; + } + } + return dp[1][n]; + } + + public static void main(String[] args) { + int test = 39; + System.out.println(ways(test)); + System.out.println(dp1(test)); + System.out.println(dp2(test)); + } + +} diff --git a/MCA算法突击课/第01期_test/Code03_SubMatrixMaxSum.java b/MCA算法突击课/第01期_test/Code03_SubMatrixMaxSum.java new file mode 100644 index 0000000..73c781d --- /dev/null +++ b/MCA算法突击课/第01期_test/Code03_SubMatrixMaxSum.java @@ -0,0 +1,75 @@ +package 第01期_test; + +public class Code03_SubMatrixMaxSum { + + public static int maxSum(int[][] m) { + if (m == null || m.length == 0 || m[0].length == 0) { + return 0; + } + // O(N^2 * M) + int N = m.length; + int M = m[0].length; + int max = Integer.MIN_VALUE; + for (int i = 0; i < N; i++) { + // i~j + int[] s = new int[M]; + for (int j = i; j < N; j++) { + for (int k = 0; k < M; k++) { + s[k] += m[j][k]; + } + max = Math.max(max, maxSubArray(s)); + } + } + return max; + } + + public static int maxSubArray(int[] arr) { + if (arr == null || arr.length == 0) { + return 0; + } + int max = Integer.MIN_VALUE; + int cur = 0; + for (int i = 0; i < arr.length; i++) { + cur += arr[i]; + max = Math.max(max, cur); + cur = cur < 0 ? 0 : cur; + } + return max; + } + + // 本题测试链接 : https://leetcode-cn.com/problems/max-submatrix-lcci/ + public static int[] getMaxMatrix(int[][] m) { + int N = m.length; + int M = m[0].length; + int max = Integer.MIN_VALUE; + int cur = 0; + int a = 0; + int b = 0; + int c = 0; + int d = 0; + for (int i = 0; i < N; i++) { + int[] s = new int[M]; + for (int j = i; j < N; j++) { + cur = 0; + int begin = 0; + for (int k = 0; k < M; k++) { + s[k] += m[j][k]; + cur += s[k]; + if (max < cur) { + max = cur; + a = i; + b = begin; + c = j; + d = k; + } + if (cur < 0) { + cur = 0; + begin = k + 1; + } + } + } + } + return new int[] { a, b, c, d }; + } + +} diff --git a/MCA算法突击课/第01期_test/Code03_TrappingRainWaterII.java b/MCA算法突击课/第01期_test/Code03_TrappingRainWaterII.java new file mode 100644 index 0000000..3fee430 --- /dev/null +++ b/MCA算法突击课/第01期_test/Code03_TrappingRainWaterII.java @@ -0,0 +1,76 @@ +package 第01期_test; + +import java.util.PriorityQueue; + +// 本题测试链接 : https://leetcode.com/problems/trapping-rain-water-ii/ +public class Code03_TrappingRainWaterII { + + public static class Node { + public int value; + public int row; + public int col; + + public Node(int v, int r, int c) { + value = v; + row = r; + col = c; + } + + } + + public static int trapRainWater(int[][] heightMap) { + if (heightMap == null || heightMap.length == 0 || heightMap[0] == null || heightMap[0].length == 0) { + return 0; + } + int N = heightMap.length; + int M = heightMap[0].length; + boolean[][] isEnter = new boolean[N][M]; + PriorityQueue heap = new PriorityQueue<>((a, b) -> a.value - b.value); + for (int col = 0; col < M - 1; col++) { + isEnter[0][col] = true; + heap.add(new Node(heightMap[0][col], 0, col)); + } + for (int row = 0; row < N - 1; row++) { + isEnter[row][M - 1] = true; + heap.add(new Node(heightMap[row][M - 1], row, M - 1)); + } + for (int col = M - 1; col > 0; col--) { + isEnter[N - 1][col] = true; + heap.add(new Node(heightMap[N - 1][col], N - 1, col)); + } + for (int row = N - 1; row > 0; row--) { + isEnter[row][0] = true; + heap.add(new Node(heightMap[row][0], row, 0)); + } + int water = 0; + int max = 0; + while (!heap.isEmpty()) { + Node cur = heap.poll(); + max = Math.max(max, cur.value); + int r = cur.row; + int c = cur.col; + if (r > 0 && !isEnter[r - 1][c]) { + water += Math.max(0, max - heightMap[r - 1][c]); + isEnter[r - 1][c] = true; + heap.add(new Node(heightMap[r - 1][c], r - 1, c)); + } + if (r < N - 1 && !isEnter[r + 1][c]) { + water += Math.max(0, max - heightMap[r + 1][c]); + isEnter[r + 1][c] = true; + heap.add(new Node(heightMap[r + 1][c], r + 1, c)); + } + if (c > 0 && !isEnter[r][c - 1]) { + water += Math.max(0, max - heightMap[r][c - 1]); + isEnter[r][c - 1] = true; + heap.add(new Node(heightMap[r][c - 1], r, c - 1)); + } + if (c < M - 1 && !isEnter[r][c + 1]) { + water += Math.max(0, max - heightMap[r][c + 1]); + isEnter[r][c + 1] = true; + heap.add(new Node(heightMap[r][c + 1], r, c + 1)); + } + } + return water; + } + +} diff --git a/MCA算法突击课/第01期_test/Code04_BestTimeToBuyAndSellStockIV.java b/MCA算法突击课/第01期_test/Code04_BestTimeToBuyAndSellStockIV.java new file mode 100644 index 0000000..0eef04e --- /dev/null +++ b/MCA算法突击课/第01期_test/Code04_BestTimeToBuyAndSellStockIV.java @@ -0,0 +1,65 @@ +package 第01期_test; + +//leetcode 188 +public class Code04_BestTimeToBuyAndSellStockIV { + + public static int maxProfit(int K, int[] prices) { + if (prices == null || prices.length == 0) { + return 0; + } + int N = prices.length; + if (K >= N / 2) { + return allTrans(prices); + } + int[][] dp = new int[K + 1][N]; + int ans = 0; + for (int tran = 1; tran <= K; tran++) { + int pre = dp[tran][0]; + int best = pre - prices[0]; + for (int index = 1; index < N; index++) { + pre = dp[tran - 1][index]; + dp[tran][index] = Math.max(dp[tran][index - 1], prices[index] + best); + best = Math.max(best, pre - prices[index]); + ans = Math.max(dp[tran][index], ans); + } + } + return ans; + } + + public static int allTrans(int[] prices) { + int ans = 0; + for (int i = 1; i < prices.length; i++) { + ans += Math.max(prices[i] - prices[i - 1], 0); + } + return ans; + } + + // 课上写的版本,对了 + public static int maxProfit2(int K, int[] arr) { + if (arr == null || arr.length == 0 || K < 1) { + return 0; + } + int N = arr.length; + if (K >= N / 2) { + return allTrans(arr); + } + int[][] dp = new int[N][K + 1]; + // dp[...][0] = 0 + // dp[0][...] = arr[0.0] 0 + for (int j = 1; j <= K; j++) { + // dp[1][j] + int p1 = dp[0][j]; + int best = Math.max(dp[1][j - 1] - arr[1], dp[0][j - 1] - arr[0]); + dp[1][j] = Math.max(p1, best + arr[1]); + // dp[1][j] 准备好一些枚举,接下来准备好的枚举 + for (int i = 2; i < N; i++) { + p1 = dp[i - 1][j]; + int newP = dp[i][j - 1] - arr[i]; + best = Math.max(newP, best); + dp[i][j] = Math.max(p1, best + arr[i]); + } + } + return dp[N - 1][K]; + } + +} diff --git a/MCA算法突击课/第01期_test/Code04_DistinctSubseq.java b/MCA算法突击课/第01期_test/Code04_DistinctSubseq.java new file mode 100644 index 0000000..de21c4d --- /dev/null +++ b/MCA算法突击课/第01期_test/Code04_DistinctSubseq.java @@ -0,0 +1,100 @@ +package 第01期_test; + +public class Code04_DistinctSubseq { + + public static int numDistinct1(String S, String T) { + char[] s = S.toCharArray(); + char[] t = T.toCharArray(); + return process(s, t, s.length, t.length); + } + + public static int process(char[] s, char[] t, int i, int j) { + if (j == 0) { + return 1; + } + if (i == 0) { + return 0; + } + int res = process(s, t, i - 1, j); + if (s[i - 1] == t[j - 1]) { + res += process(s, t, i - 1, j - 1); + } + return res; + } + + // S[...i]的所有子序列中,包含多少个字面值等于T[...j]这个字符串的子序列 + // 记为dp[i][j] + // 可能性1)S[...i]的所有子序列中,都不以s[i]结尾,则dp[i][j]肯定包含dp[i-1][j] + // 可能性2)S[...i]的所有子序列中,都必须以s[i]结尾, + // 这要求S[i] == T[j],则dp[i][j]包含dp[i-1][j-1] + public static int numDistinct2(String S, String T) { + char[] s = S.toCharArray(); + char[] t = T.toCharArray(); + // dp[i][j] : s[0..i] T[0...j] + + // dp[i][j] : s只拿前i个字符做子序列,有多少个子序列,字面值等于T的前j个字符的前缀串 + int[][] dp = new int[s.length + 1][t.length + 1]; + // dp[0][0] + // dp[0][j] = s只拿前0个字符做子序列, T前j个字符 + for (int j = 0; j <= t.length; j++) { + dp[0][j] = 0; + } + for (int i = 0; i <= s.length; i++) { + dp[i][0] = 1; + } + for (int i = 1; i <= s.length; i++) { + for (int j = 1; j <= t.length; j++) { + dp[i][j] = dp[i - 1][j] + (s[i - 1] == t[j - 1] ? dp[i - 1][j - 1] : 0); + } + } + return dp[s.length][t.length]; + } + + public static int numDistinct3(String S, String T) { + char[] s = S.toCharArray(); + char[] t = T.toCharArray(); + int[] dp = new int[t.length + 1]; + dp[0] = 1; + for (int j = 1; j <= t.length; j++) { + dp[j] = 0; + } + for (int i = 1; i <= s.length; i++) { + for (int j = t.length; j >= 1; j--) { + dp[j] += s[i - 1] == t[j - 1] ? dp[j - 1] : 0; + } + } + return dp[t.length]; + } + + public static int dp(String S, String T) { + char[] s = S.toCharArray(); + char[] t = T.toCharArray(); + int N = s.length; + int M = t.length; + int[][] dp = new int[N][M]; + // s[0..0] T[0..0] dp[0][0] + dp[0][0] = s[0] == t[0] ? 1 : 0; + for (int i = 1; i < N; i++) { + dp[i][0] = s[i] == t[0] ? (dp[i - 1][0] + 1) : dp[i - 1][0]; + } + for (int i = 1; i < N; i++) { + for (int j = 1; j <= Math.min(i, M - 1); j++) { + dp[i][j] = dp[i - 1][j]; + if (s[i] == t[j]) { + dp[i][j] += dp[i - 1][j - 1]; + } + } + } + return dp[N - 1][M - 1]; + } + + public static void main(String[] args) { + String S = "1212311112121132"; + String T = "13"; + + System.out.println(numDistinct3(S, T)); + System.out.println(dp(S, T)); + + } + +} diff --git a/MCA算法突击课/第01期_test/Code04_LongestCommonSubsequence.java b/MCA算法突击课/第01期_test/Code04_LongestCommonSubsequence.java new file mode 100644 index 0000000..bb83762 --- /dev/null +++ b/MCA算法突击课/第01期_test/Code04_LongestCommonSubsequence.java @@ -0,0 +1,67 @@ +package 第01期_test; + +// 这个问题leetcode上可以直接测 +// 链接:https://leetcode.com/problems/longest-common-subsequence/ +public class Code04_LongestCommonSubsequence { + + public static int longestCommonSubsequence1(String s1, String s2) { + if (s1 == null || s2 == null || s1.length() == 0 || s2.length() == 0) { + return 0; + } + char[] str1 = s1.toCharArray(); + char[] str2 = s2.toCharArray(); + // 尝试 + return process1(str1, str2, str1.length - 1, str2.length - 1); + } + + public static int process1(char[] str1, char[] str2, int i, int j) { + if (i == 0 && j == 0) { + return str1[i] == str2[j] ? 1 : 0; + } else if (i == 0) { + if (str1[i] == str2[j]) { + return 1; + } else { + return process1(str1, str2, i, j - 1); + } + } else if (j == 0) { + if (str1[i] == str2[j]) { + return 1; + } else { + return process1(str1, str2, i - 1, j); + } + } else { // i != 0 && j != 0 + int p1 = process1(str1, str2, i - 1, j); + int p2 = process1(str1, str2, i, j - 1); + int p3 = str1[i] == str2[j] ? (1 + process1(str1, str2, i - 1, j - 1)) : 0; + return Math.max(p1, Math.max(p2, p3)); + } + } + + public static int longestCommonSubsequence2(String s1, String s2) { + if (s1 == null || s2 == null || s1.length() == 0 || s2.length() == 0) { + return 0; + } + char[] str1 = s1.toCharArray(); + char[] str2 = s2.toCharArray(); + int N = str1.length; + int M = str2.length; + int[][] dp = new int[N][M]; + dp[0][0] = str1[0] == str2[0] ? 1 : 0; + for (int j = 1; j < M; j++) { + dp[0][j] = str1[0] == str2[j] ? 1 : dp[0][j - 1]; + } + for (int i = 1; i < N; i++) { + dp[i][0] = str1[i] == str2[0] ? 1 : dp[i - 1][0]; + } + for (int i = 1; i < N; i++) { + for (int j = 1; j < M; j++) { + int p1 = dp[i - 1][j]; + int p2 = dp[i][j - 1]; + int p3 = str1[i] == str2[j] ? (1 + dp[i - 1][j - 1]) : 0; + dp[i][j] = Math.max(p1, Math.max(p2, p3)); + } + } + return dp[N - 1][M - 1]; + } + +} diff --git a/MCA算法突击课/第01期_test/Code04_RegularExpressionMatch.java b/MCA算法突击课/第01期_test/Code04_RegularExpressionMatch.java new file mode 100644 index 0000000..ccadbe2 --- /dev/null +++ b/MCA算法突击课/第01期_test/Code04_RegularExpressionMatch.java @@ -0,0 +1,135 @@ +package 第01期_test; + +// 测试链接 : https://leetcode.com/problems/regular-expression-matching/ +public class Code04_RegularExpressionMatch { + + public static boolean isValid(char[] s, char[] e) { + // s中不能有'.' or '*' + for (int i = 0; i < s.length; i++) { + if (s[i] == '*' || s[i] == '.') { + return false; + } + } + // 开头的e[0]不能是'*',没有相邻的'*' + for (int i = 0; i < e.length; i++) { + if (e[i] == '*' && (i == 0 || e[i - 1] == '*')) { + return false; + } + } + return true; + } + + // 初始尝试版本,不包含斜率优化 + public static boolean isMatch1(String str, String exp) { + if (str == null || exp == null) { + return false; + } + char[] s = str.toCharArray(); + char[] e = exp.toCharArray(); + return isValid(s, e) && process(s, e, 0, 0); + } + + // str[si.....] 能不能被 exp[ei.....]配出来! true false + public static boolean process(char[] s, char[] e, int si, int ei) { + if (ei == e.length) { // exp 没了 str? + return si == s.length; + } + // exp[ei]还有字符 + // ei + 1位置的字符,不是* + if (ei + 1 == e.length || e[ei + 1] != '*') { + // ei + 1 不是* + // str[si] 必须和 exp[ei] 能配上! + return si != s.length && (e[ei] == s[si] || e[ei] == '.') && process(s, e, si + 1, ei + 1); + } + // exp[ei]还有字符 + // ei + 1位置的字符,是*! + while (si != s.length && (e[ei] == s[si] || e[ei] == '.')) { + if (process(s, e, si, ei + 2)) { + return true; + } + si++; + } + return process(s, e, si, ei + 2); + } + + // 改记忆化搜索+斜率优化 + public static boolean isMatch2(String str, String exp) { + if (str == null || exp == null) { + return false; + } + char[] s = str.toCharArray(); + char[] e = exp.toCharArray(); + if (!isValid(s, e)) { + return false; + } + int[][] dp = new int[s.length + 1][e.length + 1]; + // dp[i][j] = 0, 没算过! + // dp[i][j] = -1 算过,返回值是false + // dp[i][j] = 1 算过,返回值是true + return isValid(s, e) && process2(s, e, 0, 0, dp); + } + + public static boolean process2(char[] s, char[] e, int si, int ei, int[][] dp) { + if (dp[si][ei] != 0) { + return dp[si][ei] == 1; + } + boolean ans = false; + if (ei == e.length) { + ans = si == s.length; + } else { + if (ei + 1 == e.length || e[ei + 1] != '*') { + ans = si != s.length && (e[ei] == s[si] || e[ei] == '.') && process2(s, e, si + 1, ei + 1, dp); + } else { + if (si == s.length) { // ei ei+1 * + ans = process2(s, e, si, ei + 2, dp); + } else { // si没结束 + if (s[si] != e[ei] && e[ei] != '.') { + ans = process2(s, e, si, ei + 2, dp); + } else { // s[si] 可以和 e[ei]配上 + ans = process2(s, e, si, ei + 2, dp) || process2(s, e, si + 1, ei, dp); + } + } + } + } + dp[si][ei] = ans ? 1 : -1; + return ans; + } + + // 动态规划版本 + 斜率优化 + public static boolean isMatch3(String str, String pattern) { + if (str == null || pattern == null) { + return false; + } + char[] s = str.toCharArray(); + char[] p = pattern.toCharArray(); + if (!isValid(s, p)) { + return false; + } + int N = s.length; + int M = p.length; + boolean[][] dp = new boolean[N + 1][M + 1]; + dp[N][M] = true; + for (int j = M - 1; j >= 0; j--) { + dp[N][j] = (j + 1 < M && p[j + 1] == '*') && dp[N][j + 2]; + } + // dp[0..N-2][M-1]都等于false,只有dp[N-1][M-1]需要讨论 + if (N > 0 && M > 0) { + dp[N - 1][M - 1] = (s[N - 1] == p[M - 1] || p[M - 1] == '.'); + } + for (int i = N - 1; i >= 0; i--) { + for (int j = M - 2; j >= 0; j--) { + if (p[j + 1] != '*') { + dp[i][j] = ((s[i] == p[j]) || (p[j] == '.')) && dp[i + 1][j + 1]; + } else { + if ((s[i] == p[j] || p[j] == '.') && dp[i + 1][j]) { + dp[i][j] = true; + } else { + dp[i][j] = dp[i][j + 2]; + } + } + } + } + return dp[0][0]; + } + +} diff --git a/MCA算法突击课/第01期_test/Code04_SplitArrayLargestSum.java b/MCA算法突击课/第01期_test/Code04_SplitArrayLargestSum.java new file mode 100644 index 0000000..373b318 --- /dev/null +++ b/MCA算法突击课/第01期_test/Code04_SplitArrayLargestSum.java @@ -0,0 +1,193 @@ +package 第01期_test; + +// leetcode原题 +// 测试链接:https://leetcode.com/problems/split-array-largest-sum/ +public class Code04_SplitArrayLargestSum { + + // 求原数组arr[L...R]的累加和 + public static int sum(int[] sum, int L, int R) { + return sum[R + 1] - sum[L]; + } + + // 不优化枚举的动态规划方法,O(N^2 * K) + public static int splitArray1(int[] nums, int K) { + int N = nums.length; + int[] sum = new int[N + 1]; + for (int i = 0; i < N; i++) { + sum[i + 1] = sum[i] + nums[i]; + } + int[][] dp = new int[N][K + 1]; + for (int j = 1; j <= K; j++) { + dp[0][j] = nums[0]; + } + for (int i = 1; i < N; i++) { + dp[i][1] = sum(sum, 0, i); + } + // 每一行从上往下 + // 每一列从左往右 + // 根本不去凑优化位置对儿! + for (int i = 1; i < N; i++) { + for (int j = 2; j <= K; j++) { + int ans = Integer.MAX_VALUE; + // 枚举是完全不优化的! + for (int leftEnd = 0; leftEnd <= i; leftEnd++) { + int leftCost = leftEnd == -1 ? 0 : dp[leftEnd][j - 1]; + int rightCost = leftEnd == i ? 0 : sum(sum, leftEnd + 1, i); + int cur = Math.max(leftCost, rightCost); + if (cur < ans) { + ans = cur; + } + } + dp[i][j] = ans; + } + } + return dp[N - 1][K]; + } + + // 课上现场写的方法,用了枚举优化,O(N * K) + public static int splitArray2(int[] nums, int K) { + int N = nums.length; + int[] sum = new int[N + 1]; + for (int i = 0; i < N; i++) { + sum[i + 1] = sum[i] + nums[i]; + } + int[][] dp = new int[N][K + 1]; + int[][] best = new int[N][K + 1]; + for (int j = 1; j <= K; j++) { + dp[0][j] = nums[0]; + best[0][j] = -1; + } + for (int i = 1; i < N; i++) { + dp[i][1] = sum(sum, 0, i); + best[i][1] = -1; + } + // 从第2列开始,从左往右 + // 每一列,从下往上 + // 为什么这样的顺序?因为要去凑(左,下)优化位置对儿! + for (int j = 2; j <= K; j++) { + for (int i = N - 1; i >= 1; i--) { + int down = best[i][j - 1]; + // 如果i==N-1,则不优化上限 + int up = i == N - 1 ? N - 1 : best[i + 1][j]; + int ans = Integer.MAX_VALUE; + int bestChoose = -1; + for (int leftEnd = down; leftEnd <= up; leftEnd++) { + int leftCost = leftEnd == -1 ? 0 : dp[leftEnd][j - 1]; + int rightCost = leftEnd == i ? 0 : sum(sum, leftEnd + 1, i); + int cur = Math.max(leftCost, rightCost); + // 注意下面的if一定是 < 课上的错误就是此处!当时写的 <= ! + // 也就是说,只有取得明显的好处才移动! + // 举个例子来说明,比如[2,6,4,4],3个画匠时候,如下两种方案都是最优: + // (2,6) (4) 两个画匠负责 | (4) 最后一个画匠负责 + // (2,6) (4,4)两个画匠负责 | 最后一个画匠什么也不负责 + // 第一种方案划分为,[0~2] [3~3] + // 第二种方案划分为,[0~3] [无] + // 两种方案的答案都是8,但是划分点位置一定不要移动! + // 只有明显取得好处时(<),划分点位置才移动! + // 也就是说后面的方案如果==前面的最优,不要移动!只有优于前面的最优,才移动 + // 比如上面的两个方案,如果你移动到了方案二,你会得到: + // [2,6,4,4] 三个画匠时,最优为[0~3](前两个画家) [无](最后一个画家), + // 最优划分点为3位置(best[3][3]) + // 那么当4个画匠时,也就是求解dp[3][4]时 + // 因为best[3][3] = 3,这个值提供了dp[3][4]的下限 + // 而事实上dp[3][4]的最优划分为: + // [0~2](三个画家处理) [3~3] (一个画家处理),此时最优解为6 + // 所以,你就得不到dp[3][4]的最优解了,因为划分点已经越过2了 + // 提供了对数器验证,你可以改成<=,对数器和leetcode都过不了 + // 这里是<,对数器和leetcode都能通过 + // 这里面会让同学们感到困惑的点: + // 为啥==的时候,不移动,只有<的时候,才移动呢?例子懂了,但是道理何在? + // 哈哈哈哈哈,看了邮局选址问题,你更懵,请看42节! + if (cur < ans) { + ans = cur; + bestChoose = leftEnd; + } + } + dp[i][j] = ans; + best[i][j] = bestChoose; + } + } + return dp[N - 1][K]; + } + + public static int splitArray3(int[] nums, int M) { + long sum = 0; + for (int i = 0; i < nums.length; i++) { + sum += nums[i]; + } + long l = 0; + long r = sum; + long ans = 0; + while (l <= r) { + long mid = (l + r) / 2; + long cur = getNeedParts(nums, mid); + if (cur <= M) { + ans = mid; + r = mid - 1; + } else { + l = mid + 1; + } + } + return (int) ans; + } + + public static int getNeedParts(int[] arr, long aim) { + for (int i = 0; i < arr.length; i++) { + if (arr[i] > aim) { + return Integer.MAX_VALUE; + } + } + int parts = 1; + int all = arr[0]; + for (int i = 1; i < arr.length; i++) { + if (all + arr[i] > aim) { + parts++; + all = arr[i]; + } else { + all += arr[i]; + } + } + return parts; + } + + public static int[] randomArray(int len, int maxValue) { + int[] arr = new int[len]; + for (int i = 0; i < len; i++) { + arr[i] = (int) (Math.random() * maxValue); + } + return arr; + } + + public static void printArray(int[] arr) { + for (int i = 0; i < arr.length; i++) { + System.out.print(arr[i] + " "); + } + System.out.println(); + } + + public static void main(String[] args) { + int N = 100; + int maxValue = 100; + int testTime = 10000; + System.out.println("测试开始"); + for (int i = 0; i < testTime; i++) { + int len = (int) (Math.random() * N) + 1; + int M = (int) (Math.random() * N) + 1; + int[] arr = randomArray(len, maxValue); + int ans1 = splitArray1(arr, M); + int ans2 = splitArray2(arr, M); + int ans3 = splitArray3(arr, M); + if (ans1 != ans2 || ans1 != ans3) { + System.out.print("arr : "); + printArray(arr); + System.out.println("M : " + M); + System.out.println("ans1 : " + ans1); + System.out.println("ans2 : " + ans2); + System.out.println("ans3 : " + ans3); + System.out.println("Oops!"); + break; + } + } + System.out.println("测试结束"); + } +} diff --git a/MCA算法突击课/第01期_test/Code05_BestTimeToBuyAndSellStockWithCooldown.java b/MCA算法突击课/第01期_test/Code05_BestTimeToBuyAndSellStockWithCooldown.java new file mode 100644 index 0000000..aac95a8 --- /dev/null +++ b/MCA算法突击课/第01期_test/Code05_BestTimeToBuyAndSellStockWithCooldown.java @@ -0,0 +1,101 @@ +package 第01期_test; + +//leetcode 309 +public class Code05_BestTimeToBuyAndSellStockWithCooldown { + + // 该方法是对的,提交之后大量的测试用例通过,最后几个会超时 + // 如果把这个方法改成动态规划,是可以通过的,但这个尝试不是最优解 + public static int maxProfit1(int[] prices) { + return process1(prices, false, 0, 0); + } + + // buy == false 目前可以交易,而且当前没有购买行为 + // buy == true 已经买了,买入价buyPrices,待卖出 + public static int process1(int[] prices, boolean buy, int index, int buyPrices) { + if (index >= prices.length) { + return 0; + } + if (buy) { + int noSell = process1(prices, true, index + 1, buyPrices); + int yesSell = prices[index] - buyPrices + process1(prices, false, index + 2, 0); + return Math.max(noSell, yesSell); + } else { + int noBuy = process1(prices, false, index + 1, 0); + int yesBuy = process1(prices, true, index + 1, prices[index]); + return Math.max(noBuy, yesBuy); + } + } + + // 最优尝试如下: + // buy[i] : 在0...i范围上,最后一次操作是buy动作, + // 这最后一次操作有可能发生在i位置,也可能发生在i之前 + // buy[i]值的含义是:max{ 所有可能性[之前交易获得的最大收益 - 最后buy动作的收购价格] } + // 比如:arr[0...i]假设为[1,3,4,6,2,7,1...i之后的数字不用管] + // 什么叫,所有可能性[之前交易获得的最大收益 - 最后buy动作的收购价格]? + // 比如其中一种可能性: + // 假设最后一次buy动作发生在2这个数的时候,那么之前的交易只能在[1,3,4]上结束,因为6要cooldown的, + // 此时最大收益是多少呢?是4-1==3。那么,之前交易获得的最大收益 - 最后buy动作的收购价格 = 3 - 2 = 1 + // 另一种可能性: + // 再比如最后一次buy动作发生在最后的1这个数的时候,那么之前的交易只能在[1,3,4,6,2]上发生,因为7要cooldown的, + // 此时最大收益是多少呢?是6-1==5。那么,之前交易获得的最大收益 - 最后buy动作的收购价格 = 5 - 1 = 4 + // 除了上面两种可能性之外,还有很多可能性,你可以假设每个数字都是最后buy动作的时候, + // 所有可能性中,(之前交易获得的最大收益 - 最后buy动作的收购价格)的最大值,就是buy[i]的含义 + // 为啥buy[i]要算之前的最大收益 - 最后一次收购价格?尤其是最后为什么要减这么一下? + // 因为这样一来,当你之后以X价格做成一笔交易的时候,当前最好的总收益直接就是 X + buy[i]了 + // + // sell[i] :0...i范围上,最后一次操作是sell动作,这最后一次操作有可能发生在i位置,也可能发生在之前 + // sell[i]值的含义:0...i范围上,最后一次动作是sell的情况下,最好的收益 + // + // 于是通过分析,能得到以下的转移方程: + // buy[i] = Math.max(buy[i - 1], sell[i - 2] - prices[i]) + + + + + // 如果i位置没有发生buy行为,说明有没有i位置都一样,那么buy[i] = buy[i-1],这显而易见 + // 如果i位置发生了buy行为, 那么buy[i] = sell[i - 2] - prices[i], + // 因为你想在i位置买的话,你必须保证之前交易行为发生在0...i-2上, + // 因为如果i-1位置有可能参与交易的话,i位置就要cooldown了, + // 而且之前交易行为必须以sell结束,你才能buy,而且要保证之前交易尽可能得到最好的利润, + // 这正好是sell[i - 2]所代表的含义,并且根据buy[i]的定义,最后一定要 - prices[i] + // + // sell[i] = Math.max(sell[i - 1], buy[i - 1] + prices[i]) + // 如果i位置没有发生sell行为,那么sell[i] = sell[i-1],这显而易见 + // 如果i位置发生了sell行为,那么我们一定要找到 {之前获得尽可能好的收益 - 最后一次的收购价格尽可能低}, + // 而这正好是buy[i - 1]的含义!之前所有的"尽可能"中,最好的一个! + public static int maxProfit2(int[] prices) { + if (prices.length < 2) { + return 0; + } + int N = prices.length; + int[] buy = new int[N]; + int[] sell = new int[N]; + // buy[0] 不需要设置 buy[0] = -arr[0] + // sell[0] = 0 + buy[1] = Math.max(-prices[0], -prices[1]); + sell[1] = Math.max(0, prices[1] - prices[0]); + for (int i = 2; i < N; i++) { + buy[i] = Math.max(buy[i - 1], sell[i - 2] - prices[i]); + sell[i] = Math.max(sell[i - 1], buy[i - 1] + prices[i]); + } + return sell[N - 1]; + } + + // 最优解就是方法2的空间优化而已 + public static int maxProfit3(int[] prices) { + if (prices.length < 2) { + return 0; + } + int buy1 = Math.max(-prices[0], -prices[1]); + int sell1 = Math.max(0, prices[1] - prices[0]); + int sell2 = 0; + for (int i = 2; i < prices.length; i++) { + int tmp = sell1; + sell1 = Math.max(sell1, buy1 + prices[i]); + buy1 = Math.max(buy1, sell2 - prices[i]); + sell2 = tmp; + } + return sell1; + } + +} diff --git a/MCA算法突击课/第01期_test/Code05_BoatsToSavePeople.java b/MCA算法突击课/第01期_test/Code05_BoatsToSavePeople.java new file mode 100644 index 0000000..f2dd336 --- /dev/null +++ b/MCA算法突击课/第01期_test/Code05_BoatsToSavePeople.java @@ -0,0 +1,32 @@ +package 第01期_test; + +import java.util.Arrays; + +// 给定一个正数数组arr,代表若干人的体重 +// 再给定一个正数limit,表示所有船共同拥有的载重量 +// 每艘船最多坐两人,且不能超过载重 +// 想让所有的人同时过河,并且用最好的分配方法让船尽量少 +// 返回最少的船数 +// 测试链接 : https://leetcode.com/problems/boats-to-save-people/ +public class Code05_BoatsToSavePeople { + + public static int numRescueBoats(int[] people, int limit) { + Arrays.sort(people); + int ans = 0; + int l = 0; + int r = people.length - 1; + int sum = 0; + while (l <= r) { + sum = l == r ? people[l] : people[l] + people[r]; + if (sum > limit) { + r--; + } else { + l++; + r--; + } + ans++; + } + return ans; + } + +} diff --git a/MCA算法突击课/第01期_test/Code05_DistinctSubseqValue.java b/MCA算法突击课/第01期_test/Code05_DistinctSubseqValue.java new file mode 100644 index 0000000..190c516 --- /dev/null +++ b/MCA算法突击课/第01期_test/Code05_DistinctSubseqValue.java @@ -0,0 +1,50 @@ +package 第01期_test; + +import java.util.HashMap; + +// 本题测试链接 : https://leetcode.com/problems/distinct-subsequences-ii/ +public class Code05_DistinctSubseqValue { + + public static int distinctSubseqII(String s) { + if (s == null || s.length() == 0) { + return 0; + } + int m = 1000000007; + char[] str = s.toCharArray(); + int[] count = new int[26]; + int all = 1; // 算空集 + for (char x : str) { + int add = (all - count[x - 'a'] + m) % m; + all = (all + add) % m; + count[x - 'a'] = (count[x - 'a'] + add) % m; + } + return all - 1; + } + + public static int zuo(String s) { + if (s == null || s.length() == 0) { + return 0; + } + int m = 1000000007; + char[] str = s.toCharArray(); + HashMap map = new HashMap<>(); + int all = 1; // 一个字符也没遍历的时候,有空集 + for (char x : str) { + int newAdd = all; +// int curAll = all + newAdd - (map.containsKey(x) ? map.get(x) : 0); + int curAll = all; + curAll = (curAll + newAdd) % m; + curAll = (curAll - (map.containsKey(x) ? map.get(x) : 0) + m) % m; + all = curAll; + map.put(x, newAdd); + } + return all; + } + + public static void main(String[] args) { + String s = "bccaccbaabbc"; + System.out.println(distinctSubseqII(s) + 1); + System.out.println(zuo(s)); + } + +} diff --git a/MCA算法突击课/第01期_test/Code05_LongestIncreasingPath.java b/MCA算法突击课/第01期_test/Code05_LongestIncreasingPath.java new file mode 100644 index 0000000..229fdd2 --- /dev/null +++ b/MCA算法突击课/第01期_test/Code05_LongestIncreasingPath.java @@ -0,0 +1,54 @@ +package 第01期_test; + +public class Code05_LongestIncreasingPath { + + public static int longestIncreasingPath1(int[][] matrix) { + int ans = 0; + int N = matrix.length; + int M = matrix[0].length; + for (int i = 0; i < N; i++) { + for (int j = 0; j < M; j++) { + ans = Math.max(ans, process1(matrix, i, j)); + } + } + return ans; + } + + // 从m[i][j]开始走,走出来的最长递增链,返回! + public static int process1(int[][] m, int i, int j) { + int up = i > 0 && m[i][j] < m[i - 1][j] ? process1(m, i - 1, j) : 0; + int down = i < (m.length - 1) && m[i][j] < m[i + 1][j] ? process1(m, i + 1, j) : 0; + int left = j > 0 && m[i][j] < m[i][j - 1] ? process1(m, i, j - 1) : 0; + int right = j < (m[0].length - 1) && m[i][j] < m[i][j + 1] ? process1(m, i, j + 1) : 0; + return Math.max(Math.max(up, down), Math.max(left, right)) + 1; + } + + public static int longestIncreasingPath2(int[][] matrix) { + int ans = 0; + int N = matrix.length; + int M = matrix[0].length; + int[][] dp = new int[N][M]; + for (int i = 0; i < N; i++) { + for (int j = 0; j < M; j++) { + ans = Math.max(ans, process2(matrix, i, j, dp)); + } + } + return ans; + } + + // 从m[i][j]开始走,走出来的最长递增链,返回! + public static int process2(int[][] m, int i, int j, int[][] dp) { + if (dp[i][j] != 0) { + return dp[i][j]; + } + // (i,j)不越界 + int up = i > 0 && m[i][j] < m[i - 1][j] ? process2(m, i - 1, j, dp) : 0; + int down = i < (m.length - 1) && m[i][j] < m[i + 1][j] ? process2(m, i + 1, j, dp) : 0; + int left = j > 0 && m[i][j] < m[i][j - 1] ? process2(m, i, j - 1, dp) : 0; + int right = j < (m[0].length - 1) && m[i][j] < m[i][j + 1] ? process2(m, i, j + 1, dp) : 0; + int ans = Math.max(Math.max(up, down), Math.max(left, right)) + 1; + dp[i][j] = ans; + return ans; + } + +} diff --git a/MCA算法突击课/第01期_test/Code05_MinWindowLength.java b/MCA算法突击课/第01期_test/Code05_MinWindowLength.java new file mode 100644 index 0000000..4f056f2 --- /dev/null +++ b/MCA算法突击课/第01期_test/Code05_MinWindowLength.java @@ -0,0 +1,80 @@ +package 第01期_test; + +public class Code05_MinWindowLength { + + public static int minLength(String s1, String s2) { + if (s1 == null || s2 == null || s1.length() < s2.length()) { + return Integer.MAX_VALUE; + } + char[] str1 = s1.toCharArray(); + char[] str2 = s2.toCharArray(); + int[] map = new int[256]; // map[37] = 4 37 4次 + for (int i = 0; i != str2.length; i++) { + map[str2[i]]++; + } + int all = str2.length; + + // [L,R-1] R + // [L,R) -> [0,0) + int L = 0; + int R = 0; + int minLen = Integer.MAX_VALUE; + while (R != str1.length) { + map[str1[R]]--; + if (map[str1[R]] >= 0) { + all--; + } + if (all == 0) { // 还完了 + while (map[str1[L]] < 0) { + map[str1[L++]]++; + } + // [L..R] + minLen = Math.min(minLen, R - L + 1); + all++; + map[str1[L++]]++; + } + R++; + } + return minLen == Integer.MAX_VALUE ? 0 : minLen; + } + + // 测试链接 : https://leetcode.com/problems/minimum-window-substring/ + public static String minWindow(String s, String t) { + if (s.length() < t.length()) { + return ""; + } + char[] str = s.toCharArray(); + char[] target = t.toCharArray(); + int[] map = new int[256]; + for (char cha : target) { + map[cha]++; + } + int all = target.length; + int L = 0; + int R = 0; + int minLen = Integer.MAX_VALUE; + int ansl = -1; + int ansr = -1; + while (R != str.length) { + map[str[R]]--; + if (map[str[R]] >= 0) { + all--; + } + if (all == 0) { + while (map[str[L]] < 0) { + map[str[L++]]++; + } + if (minLen > R - L + 1) { + minLen = R - L + 1; + ansl = L; + ansr = R; + } + all++; + map[str[L++]]++; + } + R++; + } + return minLen == Integer.MAX_VALUE ? "" : s.substring(ansl, ansr + 1); + } + +} diff --git a/MCA算法突击课/第01期_test/Code05_WorldBreak.java b/MCA算法突击课/第01期_test/Code05_WorldBreak.java new file mode 100644 index 0000000..52cb37f --- /dev/null +++ b/MCA算法突击课/第01期_test/Code05_WorldBreak.java @@ -0,0 +1,237 @@ +package 第01期_test; + +import java.util.HashSet; + +public class Code05_WorldBreak { + /* + * + * 假设所有字符都是小写字母. 大字符串是str. arr是去重的单词表, 每个单词都不是空字符串且可以使用任意次. + * 使用arr中的单词有多少种拼接str的方式. 返回方法数. + * + */ + + public static int ways(String str, String[] arr) { + HashSet set = new HashSet<>(); + for (String candidate : arr) { + set.add(candidate); + } + return process(str, 0, set); + } + + // 所有的可分解字符串,都已经放在了set中 + // str[i....] 能够被set中的贴纸分解的话,返回分解的方法数 + public static int process(String str, int i, HashSet set) { + if (i == str.length()) { // 没字符串需要分解了! + return 1; + } + // i....还有字符串需要分解 + int ways = 0; + // [i ... end] 前缀串 每一个前缀串 + for (int end = i; end < str.length(); end++) { + String pre = str.substring(i, end + 1);// [) + if (set.contains(pre)) { + ways += process(str, end + 1, set); + } + } + return ways; + } + + public static int ways1(String str, String[] arr) { + if (str == null || str.length() == 0 || arr == null || arr.length == 0) { + return 0; + } + HashSet map = new HashSet<>(); + for (String s : arr) { + map.add(s); + } + return f(str, map, 0); + } + + public static int f(String str, HashSet map, int index) { + if (index == str.length()) { + return 1; + } + int ways = 0; + for (int end = index; end < str.length(); end++) { + if (map.contains(str.substring(index, end + 1))) { + ways += f(str, map, end + 1); + } + } + return ways; + } + + public static int ways2(String str, String[] arr) { + if (str == null || str.length() == 0 || arr == null || arr.length == 0) { + return 0; + } + HashSet map = new HashSet<>(); + for (String s : arr) { + map.add(s); + } + int N = str.length(); + int[] dp = new int[N + 1]; + dp[N] = 1; + for (int i = N - 1; i >= 0; i--) { + for (int end = i; end < N; end++) { + if (map.contains(str.substring(i, end + 1))) { + dp[i] += dp[end + 1]; + } + } + } + return dp[0]; + } + + public static class Node { + public boolean end; + public Node[] nexts; + + public Node() { + end = false; + nexts = new Node[26]; + } + } + + public static int ways3(String str, String[] arr) { + if (str == null || str.length() == 0 || arr == null || arr.length == 0) { + return 0; + } + Node root = new Node(); + for (String s : arr) { + char[] chs = s.toCharArray(); + Node node = root; + int index = 0; + for (int i = 0; i < chs.length; i++) { + index = chs[i] - 'a'; + if (node.nexts[index] == null) { + node.nexts[index] = new Node(); + } + node = node.nexts[index]; + } + node.end = true; + } + return g(str.toCharArray(), root, 0); + } + + // str[i...] 被分解的方法数,返回 + + public static int g(char[] str, Node root, int i) { + if (i == str.length) { + return 1; + } + int ways = 0; + Node cur = root; + // i...end + for (int end = i; end < str.length; end++) { + int path = str[end] - 'a'; + if (cur.nexts[path] == null) { + break; + } + cur = cur.nexts[path]; + if (cur.end) { // i...end + ways += g(str, root, end + 1); + } + } + return ways; + } + + public static int ways4(String s, String[] arr) { + if (s == null || s.length() == 0 || arr == null || arr.length == 0) { + return 0; + } + Node root = new Node(); + for (String str : arr) { + char[] chs = str.toCharArray(); + Node node = root; + int index = 0; + for (int i = 0; i < chs.length; i++) { + index = chs[i] - 'a'; + if (node.nexts[index] == null) { + node.nexts[index] = new Node(); + } + node = node.nexts[index]; + } + node.end = true; + } + char[] str = s.toCharArray(); + int N = str.length; + int[] dp = new int[N + 1]; + dp[N] = 1; + for (int i = N - 1; i >= 0; i--) { + Node cur = root; + for (int end = i; end < N; end++) { + int path = str[end] - 'a'; + if (cur.nexts[path] == null) { + break; + } + cur = cur.nexts[path]; + if (cur.end) { + dp[i] += dp[end + 1]; + } + } + } + return dp[0]; + } + + // 以下的逻辑都是为了测试 + public static class RandomSample { + public String str; + public String[] arr; + + public RandomSample(String s, String[] a) { + str = s; + arr = a; + } + } + + // 随机样本产生器 + public static RandomSample generateRandomSample(char[] candidates, int num, int len, int joint) { + String[] seeds = randomSeeds(candidates, num, len); + HashSet set = new HashSet<>(); + for (String str : seeds) { + set.add(str); + } + String[] arr = new String[set.size()]; + int index = 0; + for (String str : set) { + arr[index++] = str; + } + StringBuilder all = new StringBuilder(); + for (int i = 0; i < joint; i++) { + all.append(arr[(int) (Math.random() * arr.length)]); + } + return new RandomSample(all.toString(), arr); + } + + public static String[] randomSeeds(char[] candidates, int num, int len) { + String[] arr = new String[(int) (Math.random() * num) + 1]; + for (int i = 0; i < arr.length; i++) { + char[] str = new char[(int) (Math.random() * len) + 1]; + for (int j = 0; j < str.length; j++) { + str[j] = candidates[(int) (Math.random() * candidates.length)]; + } + arr[i] = String.valueOf(str); + } + return arr; + } + + public static void main(String[] args) { + char[] candidates = { 'a', 'b' }; + int num = 20; + int len = 4; + int joint = 5; + int testTimes = 30000; + boolean testResult = true; + for (int i = 0; i < testTimes; i++) { + RandomSample sample = generateRandomSample(candidates, num, len, joint); + int ans1 = ways1(sample.str, sample.arr); + int ans2 = ways2(sample.str, sample.arr); + int ans3 = ways3(sample.str, sample.arr); + int ans4 = ways4(sample.str, sample.arr); + if (ans1 != ans2 || ans3 != ans4 || ans2 != ans4) { + testResult = false; + } + } + System.out.println(testTimes + "次随机测试是否通过:" + testResult); + } + +} diff --git a/MCA算法突击课/第01期_test/Code06_BestTimeToBuyAndSellStockWithTransactionFee.java b/MCA算法突击课/第01期_test/Code06_BestTimeToBuyAndSellStockWithTransactionFee.java new file mode 100644 index 0000000..91c8280 --- /dev/null +++ b/MCA算法突击课/第01期_test/Code06_BestTimeToBuyAndSellStockWithTransactionFee.java @@ -0,0 +1,27 @@ +package 第01期_test; + +//leetcode 714 +public class Code06_BestTimeToBuyAndSellStockWithTransactionFee { + + public static int maxProfit(int[] arr, int fee) { + if (arr == null || arr.length < 2) { + return 0; + } + int N = arr.length; + // 0..0 0 -[0] - fee + int bestbuy = -arr[0] - fee; + // 0..0 卖 0 + int bestsell = 0; + for (int i = 1; i < N; i++) { + // 来到i位置了! + // 如果在i必须买 收入 - 批发价 - fee + int curbuy = bestsell - arr[i] - fee; + // 如果在i必须卖 整体最优(收入 - 良好批发价 - fee) + int cursell = bestbuy + arr[i]; + bestbuy = Math.max(bestbuy, curbuy); + bestsell = Math.max(bestsell, cursell); + } + return bestsell; + } + +} diff --git a/MCA算法突击课/第01期_test/Code06_ClosestSubsequenceSum.java b/MCA算法突击课/第01期_test/Code06_ClosestSubsequenceSum.java new file mode 100644 index 0000000..52269f4 --- /dev/null +++ b/MCA算法突击课/第01期_test/Code06_ClosestSubsequenceSum.java @@ -0,0 +1,46 @@ +package 第01期_test; + +import java.util.Arrays; + +// 本题测试链接 : https://leetcode.com/problems/closest-subsequence-sum/ +// 本题数据量描述: +// 1 <= nums.length <= 40 +// -10^7 <= nums[i] <= 10^7 +// -10^9 <= goal <= 10^9 +// 通过这个数据量描述可知,需要用到分治,因为数组长度不大 +// 而值很大,用动态规划的话,表会爆 +public class Code06_ClosestSubsequenceSum { + + public static int[] l = new int[1 << 20]; + public static int[] r = new int[1 << 20]; + + public static int minAbsDifference(int[] nums, int goal) { + if (nums == null || nums.length == 0) { + return goal; + } + int le = process(nums, 0, nums.length >> 1, 0, 0, l); + int re = process(nums, nums.length >> 1, nums.length, 0, 0, r); + Arrays.sort(l, 0, le); + Arrays.sort(r, 0, re--); + int ans = Math.abs(goal); + for (int i = 0; i < le; i++) { + int rest = goal - l[i]; + while (re > 0 && Math.abs(rest - r[re - 1]) <= Math.abs(rest - r[re])) { + re--; + } + ans = Math.min(ans, Math.abs(rest - r[re])); + } + return ans; + } + + public static int process(int[] nums, int index, int end, int sum, int fill, int[] arr) { + if (index == end) { + arr[fill++] = sum; + } else { + fill = process(nums, index + 1, end, sum, fill, arr); + fill = process(nums, index + 1, end, sum + nums[index], fill, arr); + } + return fill; + } + +} diff --git a/MCA算法突击课/第01期_test/Code07_MinLengthForSort.java b/MCA算法突击课/第01期_test/Code07_MinLengthForSort.java new file mode 100644 index 0000000..9ca6179 --- /dev/null +++ b/MCA算法突击课/第01期_test/Code07_MinLengthForSort.java @@ -0,0 +1,30 @@ +package 第01期_test; + +// 本题测试链接 : https://leetcode.com/problems/shortest-unsorted-continuous-subarray/ +public class Code07_MinLengthForSort { + + public static int findUnsortedSubarray(int[] nums) { + if (nums == null || nums.length < 2) { + return 0; + } + int N = nums.length; + int right = -1; + int max = Integer.MIN_VALUE; + for (int i = 0; i < N; i++) { + if (max > nums[i]) { + right = i; + } + max = Math.max(max, nums[i]); + } + int min = Integer.MAX_VALUE; + int left = N; + for (int i = N - 1; i >= 0; i--) { + if (min < nums[i]) { + left = i; + } + min = Math.min(min, nums[i]); + } + return Math.max(0, right - left + 1); + } + +} diff --git a/MCA算法突击课/第01期_test/Code07_MoneyProblem.java b/MCA算法突击课/第01期_test/Code07_MoneyProblem.java new file mode 100644 index 0000000..fd57ca8 --- /dev/null +++ b/MCA算法突击课/第01期_test/Code07_MoneyProblem.java @@ -0,0 +1,169 @@ +package 第01期_test; + +public class Code07_MoneyProblem { + + // int[] d d[i]:i号怪兽的武力 + // int[] p p[i]:i号怪兽要求的钱 + // ability 当前你所具有的能力 + // index 来到了第index个怪兽的面前 + + // 目前,你的能力是ability,你来到了index号怪兽的面前,如果要通过后续所有的怪兽, + // 请返回需要花的最少钱数 + public static long process1(int[] d, int[] p, int ability, int index) { + if (index == d.length) { + return 0; + } + if (ability < d[index]) { + return p[index] + process1(d, p, ability + d[index], index + 1); + } else { // ability >= d[index] 可以贿赂,也可以不贿赂 + return Math.min( + + p[index] + process1(d, p, ability + d[index], index + 1), + + 0 + process1(d, p, ability, index + 1)); + } + } + + public static long func1(int[] d, int[] p) { + return process1(d, p, 0, 0); + } + + // 从0....index号怪兽,花的钱,必须严格==money + // 如果通过不了,返回-1 + // 如果可以通过,返回能通过情况下的最大能力值 + public static long process2(int[] d, int[] p, int index, int money) { + if (index == -1) { // 一个怪兽也没遇到呢 + return money == 0 ? 0 : -1; + } + // index >= 0 + // 1) 不贿赂当前index号怪兽 + long preMaxAbility = process2(d, p, index - 1, money); + long p1 = -1; + if (preMaxAbility != -1 && preMaxAbility >= d[index]) { + p1 = preMaxAbility; + } + // 2) 贿赂当前的怪兽 当前的钱 p[index] + long preMaxAbility2 = process2(d, p, index - 1, money - p[index]); + long p2 = -1; + if (preMaxAbility2 != -1) { + p2 = d[index] + preMaxAbility2; + } + return Math.max(p1, p2); + } + + public static int minMoney2(int[] d, int[] p) { + int allMoney = 0; + for (int i = 0; i < p.length; i++) { + allMoney += p[i]; + } + int N = d.length; + for (int money = 0; money < allMoney; money++) { + if (process2(d, p, N - 1, money) != -1) { + return money; + } + } + return allMoney; + } + + 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 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 = 10000; + 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); + long ans4 = minMoney2(d,p); + if (ans1 != ans2 || ans2 != ans3 || ans1 != ans4) { + System.out.println("oops!"); + } + } + + } + +} diff --git a/MCA算法突击课/第01期_test/Code07_TargetSum.java b/MCA算法突击课/第01期_test/Code07_TargetSum.java new file mode 100644 index 0000000..20fbe16 --- /dev/null +++ b/MCA算法突击课/第01期_test/Code07_TargetSum.java @@ -0,0 +1,127 @@ +package 第01期_test; + +import java.util.HashMap; + +// leetcode 494题 +public class Code07_TargetSum { + + public static int findTargetSumWays1(int[] arr, int s) { + return process1(arr, 0, s); + } + + // 可以自由使用arr[index....]所有的数字! + // 搞出rest这个数,方法数是多少?返回 + // index == 7 rest = 13 + // map "7_13" 256 + public static int process1(int[] arr, int index, int rest) { + if (index == arr.length) { // 没数了! + return rest == 0 ? 1 : 0; + } + // 还有数!arr[index] arr[index+1 ... ] + return process1(arr, index + 1, rest - arr[index]) + process1(arr, index + 1, rest + arr[index]); + } + + public static int findTargetSumWays2(int[] arr, int s) { + return process2(arr, 0, s, new HashMap<>()); + } + + public static int process2(int[] arr, int index, int rest, HashMap> dp) { + if (dp.containsKey(index) && dp.get(index).containsKey(rest)) { + return dp.get(index).get(rest); + } + // 否则,没命中! + int ans = 0; + if (index == arr.length) { + ans = rest == 0 ? 1 : 0; + } else { + ans = process2(arr, index + 1, rest - arr[index], dp) + process2(arr, index + 1, rest + arr[index], dp); + } + if (!dp.containsKey(index)) { + dp.put(index, new HashMap<>()); + } + dp.get(index).put(rest, ans); + return ans; + } + + // 优化点一 : + // 你可以认为arr中都是非负数 + // 因为即便是arr中有负数,比如[3,-4,2] + // 因为你能在每个数前面用+或者-号 + // 所以[3,-4,2]其实和[3,4,2]达成一样的效果 + // 那么我们就全把arr变成非负数,不会影响结果的 + // 优化点二 : + // 如果arr都是非负数,并且所有数的累加和是sum + // 那么如果target> 1); + } + + // 求非负数组nums有多少个子集,累加和是s + // 二维动态规划 + // 不用空间压缩 + public static int subset1(int[] nums, int s) { + if (s < 0) { + return 0; + } + int n = nums.length; + // dp[i][j] : nums前缀长度为i的所有子集,有多少累加和是j? + int[][] dp = new int[n + 1][s + 1]; + // nums前缀长度为0的所有子集,有多少累加和是0?一个:空集 + dp[0][0] = 1; + for (int i = 1; i <= n; i++) { + for (int j = 0; j <= s; j++) { + dp[i][j] = dp[i - 1][j]; + if (j - nums[i - 1] >= 0) { + dp[i][j] += dp[i - 1][j - nums[i - 1]]; + } + } + } + return dp[n][s]; + } + + // 求非负数组nums有多少个子集,累加和是s + // 二维动态规划 + // 用空间压缩: + // 核心就是for循环里面的:for (int i = s; i >= n; i--) { + // 为啥不枚举所有可能的累加和?只枚举 n...s 这些累加和? + // 因为如果 i - n < 0,dp[i]怎么更新?和上一步的dp[i]一样!所以不用更新 + // 如果 i - n >= 0,dp[i]怎么更新?上一步的dp[i] + 上一步dp[i - n]的值,这才需要更新 + public static int subset2(int[] nums, int s) { + if (s < 0) { + return 0; + } + int[] dp = new int[s + 1]; + dp[0] = 1; + for (int n : nums) { + for (int i = s; i >= n; i--) { + dp[i] += dp[i - n]; + } + } + return dp[s]; + } + +} diff --git a/MCA算法突击课/第01期_test/Code11_StoneGameIV.java b/MCA算法突击课/第01期_test/Code11_StoneGameIV.java new file mode 100644 index 0000000..228a8a5 --- /dev/null +++ b/MCA算法突击课/第01期_test/Code11_StoneGameIV.java @@ -0,0 +1,63 @@ +package 第01期_test; + +// 来自哈喽单车 +// 本题是leetcode原题 : https://leetcode.com/problems/stone-game-iv/ +public class Code11_StoneGameIV { + + // 当前的!先手,会不会赢 + // 打表,不能发现规律 + public static boolean winnerSquareGame1(int n) { + if (n == 0) { + return false; + } + // 当前的先手,会尝试所有的情况,1,4,9,16,25,36.... + for (int i = 1; i * i <= n; i++) { + // 当前的先手,决定拿走 i * i 这个平方数 + // 它的对手会不会赢? winnerSquareGame1(n - i * i) + if (!winnerSquareGame1(n - i * i)) { + return true; + } + } + return false; + } + + public static boolean winnerSquareGame2(int n) { + int[] dp = new int[n + 1]; + dp[0] = -1; + return process2(n, dp); + } + + public static boolean process2(int n, int[] dp) { + if (dp[n] != 0) { + return dp[n] == 1 ? true : false; + } + boolean ans = false; + for (int i = 1; i * i <= n; i++) { + if (!process2(n - i * i, dp)) { + ans = true; + break; + } + } + dp[n] = ans ? 1 : -1; + return ans; + } + + public static boolean winnerSquareGame3(int n) { + boolean[] dp = new boolean[n + 1]; + for (int i = 1; i <= n; i++) { + for (int j = 1; j * j <= i; j++) { + if (!dp[i - j * j]) { + dp[i] = true; + break; + } + } + } + return dp[n]; + } + + public static void main(String[] args) { + int n = 10000000; + System.out.println(winnerSquareGame3(n)); + } + +} diff --git a/MCA算法突击课/第01期_test/Problem_0152_MaximumProductSubarray.java b/MCA算法突击课/第01期_test/Problem_0152_MaximumProductSubarray.java new file mode 100644 index 0000000..966570f --- /dev/null +++ b/MCA算法突击课/第01期_test/Problem_0152_MaximumProductSubarray.java @@ -0,0 +1,45 @@ +package 第01期_test; + +public class Problem_0152_MaximumProductSubarray { + + + public static double max(double[] arr) { + if(arr == null || arr.length == 0) { + return 0; // 报错! + } + int n = arr.length; + // 上一步的最大 + double premax = arr[0]; + // 上一步的最小 + double premin = arr[0]; + double ans = arr[0]; + for(int i = 1; i < n; i++) { + double p1 = arr[i]; + double p2 = arr[i] * premax; + double p3 = arr[i] * premin; + double curmax = Math.max(Math.max(p1, p2), p3); + double curmin = Math.min(Math.min(p1, p2), p3); + ans = Math.max(ans, curmax); + premax = curmax; + premin = curmin; + } + return ans; + } + + + + public static int maxProduct(int[] nums) { + int ans = nums[0]; + int min = nums[0]; + int max = nums[0]; + for (int i = 1; i < nums.length; i++) { + int curmin = Math.min(nums[i], Math.min(min * nums[i], max * nums[i])); + int curmax = Math.max(nums[i], Math.max(min * nums[i], max * nums[i])); + min = curmin; + max = curmax; + ans = Math.max(ans, max); + } + return ans; + } + +} diff --git a/MCA算法突击课/第01期_test/Problem_0213_HouseRobberII.java b/MCA算法突击课/第01期_test/Problem_0213_HouseRobberII.java new file mode 100644 index 0000000..600c821 --- /dev/null +++ b/MCA算法突击课/第01期_test/Problem_0213_HouseRobberII.java @@ -0,0 +1,50 @@ +package 第01期_test; + +public class Problem_0213_HouseRobberII { + + // arr 长度大于等于1 + public static int pickMaxSum(int[] arr) { + int n = arr.length; + // dp[i] : arr[0..i]范围上,随意选择,但是,任何两数不能相邻。得到的最大累加和是多少? + int[] dp = new int[n]; + dp[0] = arr[0]; + dp[1] = Math.max(arr[0], arr[1]); + for (int i = 2; i < n; i++) { + int p1 = arr[i]; + int p2 = dp[i - 1]; + int p3 = arr[i] + dp[i - 2]; + dp[i] = Math.max(p1, Math.max(p2, p3)); + } + return dp[n - 1]; + } + + public static int rob(int[] nums) { + if (nums == null || nums.length == 0) { + return 0; + } + if (nums.length == 1) { + return nums[0]; + } + if (nums.length == 2) { + return Math.max(nums[0], nums[1]); + } + int pre2 = nums[0]; + int pre1 = Math.max(nums[0], nums[1]); + for (int i = 2; i < nums.length - 1; i++) { + int tmp = Math.max(pre1, nums[i] + pre2); + pre2 = pre1; + pre1 = tmp; + } + int ans1 = pre1; + pre2 = nums[1]; + pre1 = Math.max(nums[1], nums[2]); + for (int i = 3; i < nums.length; i++) { + int tmp = Math.max(pre1, nums[i] + pre2); + pre2 = pre1; + pre1 = tmp; + } + int ans2 = pre1; + return Math.max(ans1, ans2); + } + +} diff --git a/MCA算法突击课/第01期_test/Problem_0265_PaintHouseII.java b/MCA算法突击课/第01期_test/Problem_0265_PaintHouseII.java new file mode 100644 index 0000000..92ca999 --- /dev/null +++ b/MCA算法突击课/第01期_test/Problem_0265_PaintHouseII.java @@ -0,0 +1,56 @@ +package 第01期_test; + +public class Problem_0265_PaintHouseII { + + // costs[i][k] i号房子用k颜色刷的花费 + // 要让0...N-1的房子相邻不同色 + // 返回最小花费 + public static int minCostII(int[][] costs) { + int N = costs.length; + if (N == 0) { + return 0; + } + int K = costs[0].length; + // 之前取得的最小代价、取得最小代价时的颜色 + int preMin1 = 0; + int preEnd1 = -1; + // 之前取得的次小代价、取得次小代价时的颜色 + int preMin2 = 0; + int preEnd2 = -1; + for (int i = 0; i < N; i++) { // i房子 + int curMin1 = Integer.MAX_VALUE; + int curEnd1 = -1; + int curMin2 = Integer.MAX_VALUE; + int curEnd2 = -1; + for (int j = 0; j < K; j++) { // j颜色! + if (j != preEnd1) { + if (preMin1 + costs[i][j] < curMin1) { + curMin2 = curMin1; + curEnd2 = curEnd1; + curMin1 = preMin1 + costs[i][j]; + curEnd1 = j; + } else if (preMin1 + costs[i][j] < curMin2) { + curMin2 = preMin1 + costs[i][j]; + curEnd2 = j; + } + } else if (j != preEnd2) { + if (preMin2 + costs[i][j] < curMin1) { + curMin2 = curMin1; + curEnd2 = curEnd1; + curMin1 = preMin2 + costs[i][j]; + curEnd1 = j; + } else if (preMin2 + costs[i][j] < curMin2) { + curMin2 = preMin2 + costs[i][j]; + curEnd2 = j; + } + } + } + preMin1 = curMin1; + preEnd1 = curEnd1; + preMin2 = curMin2; + preEnd2 = curEnd2; + } + return preMin1; + } + +} diff --git a/MCA算法突击课/第01期_test/Problem_0395_LongestSubstringWithAtLeastKRepeatingCharacters.java b/MCA算法突击课/第01期_test/Problem_0395_LongestSubstringWithAtLeastKRepeatingCharacters.java new file mode 100644 index 0000000..b950d46 --- /dev/null +++ b/MCA算法突击课/第01期_test/Problem_0395_LongestSubstringWithAtLeastKRepeatingCharacters.java @@ -0,0 +1,110 @@ +package 第01期_test; + +public class Problem_0395_LongestSubstringWithAtLeastKRepeatingCharacters { + + public static int longestSubstring1(String s, int k) { + char[] str = s.toCharArray(); + int N = str.length; + int max = 0; + for (int i = 0; i < N; i++) { + int[] count = new int[256]; + int collect = 0; + int satisfy = 0; + for (int j = i; j < N; j++) { + if (count[str[j]] == 0) { + collect++; + } + if (count[str[j]] == k - 1) { + satisfy++; + } + count[str[j]]++; + if (collect == satisfy) { + max = Math.max(max, j - i + 1); + } + } + } + return max; + } + + public static int longestSubstring2(String s, int k) { + char[] str = s.toCharArray(); + int N = str.length; + int max = 0; + for (int require = 1; require <= 26; require++) { + // 3种 + // a~z 出现次数 + int[] count = new int[26]; + // 目前窗口内收集了几种字符了 + int collect = 0; + // 目前窗口内出现次数>=k次的字符,满足了几种 + int satisfy = 0; + // 窗口右边界 + int R = -1; + for (int L = 0; L < N; L++) { // L要尝试每一个窗口的最左位置 + // [L..R] R+1 + while (R + 1 < N && !(collect == require && count[str[R + 1] - 'a'] == 0)) { + R++; + if (count[str[R] - 'a'] == 0) { + collect++; + } + if (count[str[R] - 'a'] == k - 1) { + satisfy++; + } + count[str[R] - 'a']++; + } + // [L...R] + if (satisfy == require) { + max = Math.max(max, R - L + 1); + } + // L++ + if (count[str[L] - 'a'] == 1) { + collect--; + } + if (count[str[L] - 'a'] == k) { + satisfy--; + } + count[str[L] - 'a']--; + } + } + return max; + } + + // 会超时,但是思路的确是正确的 + public static int longestSubstring3(String s, int k) { + return process(s.toCharArray(), 0, s.length() - 1, k); + } + + public static int process(char[] str, int L, int R, int k) { + if (L > R) { + return 0; + } + int[] counts = new int[26]; + for (int i = L; i <= R; i++) { + counts[str[i] - 'a']++; + } + char few = 0; + int min = Integer.MAX_VALUE; + for (int i = 0; i < 26; i++) { + if (counts[i] != 0 && min > counts[i]) { + few = (char) (i + 'a'); + min = counts[i]; + } + } + if (min >= k) { + return R - L + 1; + } + int pre = 0; + int max = Integer.MIN_VALUE; + for (int i = L; i <= R; i++) { + if (str[i] == few) { + max = Math.max(max, process(str, pre, i - 1, k)); + pre = i + 1; + } + } + if (pre != R + 1) { + max = Math.max(max, process(str, pre, R, k)); + } + return max; + } + +} diff --git a/MCA算法突击课/第01期_test/Problem_1488_AvoidFloodInTheCity.java b/MCA算法突击课/第01期_test/Problem_1488_AvoidFloodInTheCity.java new file mode 100644 index 0000000..b85de64 --- /dev/null +++ b/MCA算法突击课/第01期_test/Problem_1488_AvoidFloodInTheCity.java @@ -0,0 +1,81 @@ +package 第01期_test; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.PriorityQueue; + +public class Problem_1488_AvoidFloodInTheCity { + + // rains[i] = j 第i天轮到j号湖泊下雨 + // 规定,下雨日,干啥 : -1 + // 不下雨日,如果没有湖泊可抽 : 1 + public static int[] avoidFlood(int[] rains) { + int n = rains.length; + int[] ans = new int[n]; + int[] invalid = new int[0]; + // key : 某个湖 + // value : 这个湖在哪些位置降雨 + // 4 : {3,7,19,21} + // 1 : { 13 } + // 2 : {4, 56} + HashMap> map = new HashMap<>(); + for (int i = 0; i < n; i++) { + if (rains[i] != 0) { // 第i天要下雨,rains[i] + // 3天 9号 + // 9号 { 3 } + // 9号 {1, 3} + if (!map.containsKey(rains[i])) { + map.put(rains[i], new LinkedList<>()); + } + map.get(rains[i]).addLast(i); + } + } + // 没抽干的湖泊表 + // 某个湖如果满了,加入到set里 + // 某个湖被抽干了,从set中移除 + HashSet set = new HashSet<>(); + // 这个堆的堆顶表示最先处理的湖是哪个 + PriorityQueue heap = new PriorityQueue<>(); + for (int i = 0; i < n; i++) { // 0 1 2 3 ... + if (rains[i] != 0) { + if (set.contains(rains[i])) { + return invalid; + } + // 放入到没抽干的表里 + set.add(rains[i]); + map.get(rains[i]).pollFirst(); + if (!map.get(rains[i]).isEmpty()) { + heap.add(new Work(rains[i], map.get(rains[i]).peekFirst())); + } + // 题目规定 + ans[i] = -1; + } else { // 今天干活! + if (heap.isEmpty()) { + ans[i] = 1; + } else { + Work cur = heap.poll(); + set.remove(cur.lake); + ans[i] = cur.lake; + } + } + } + return ans; + } + + public static class Work implements Comparable { + public int lake; + public int nextRain; + + public Work(int l, int p) { + lake = l; + nextRain = p; + } + + @Override + public int compareTo(Work o) { + return nextRain - o.nextRain; + } + } + +}