modify code

master
algorithmzuo 11 months ago
parent e8d2eae9e7
commit a64b46ff5a

@ -0,0 +1,72 @@
package class_2023_07_4_week;
import java.util.PriorityQueue;
// 给你一个正整数数组 nums
// 每一次操作中,你可以从 nums 中选择 任意 一个数并将它减小到 恰好 一半
//(注意,在后续操作中你可以对减半过的数继续执行操作)
// 请你返回将 nums 数组和 至少 减少一半的 最少 操作数
// 测试链接 : https://leetcode.cn/problems/minimum-operations-to-halve-array-sum/
public class Code01_MinimumOperationsToHalveArraySum {
public static int halveArray1(int[] nums) {
PriorityQueue<Double> heap = new PriorityQueue<>((a, b) -> b.compareTo(a));
double sum = 0;
for (int num : nums) {
heap.add((double) num);
sum += num;
}
sum /= 2;
int ans = 0;
for (double minus = 0, cur; minus < sum; ans++, minus += cur) {
cur = heap.poll() / 2;
heap.add(cur);
}
return ans;
}
public static int MAXN = 100001;
public static long[] heap = new long[MAXN];
public static int size;
public static int halveArray2(int[] nums) {
size = nums.length;
long sum = 0;
for (int i = size - 1; i >= 0; i--) {
heap[i] = (long) nums[i] << 20;
sum += heap[i];
heapify(i);
}
sum /= 2;
int ans = 0;
for (long minus = 0; minus < sum; ans++) {
heap[0] /= 2;
minus += heap[0];
heapify(0);
}
return ans;
}
public static void heapify(int i) {
int l = i * 2 + 1;
while (l < size) {
int best = l + 1 < size && heap[l + 1] > heap[l] ? l + 1 : l;
best = heap[best] > heap[i] ? best : i;
if (best == i) {
break;
}
swap(best, i);
i = best;
l = i * 2 + 1;
}
}
public static void swap(int i, int j) {
long tmp = heap[i];
heap[i] = heap[j];
heap[j] = tmp;
}
}

@ -0,0 +1,86 @@
package class_2023_07_4_week;
// 自 01背包问世之后小 A 对此深感兴趣
// 一天,小 A 去远游,却发现他的背包不同于 01 背包,他的物品大致可分为 k 组
// 每组中的物品只能选择1件现在他想知道最大的利用价值是多少
// 测试链接 : www.luogu.com.cn/problem/P1757
// 请同学们务必参考如下代码中关于输入、输出的处理
// 这是输入输出处理效率很高的写法
// 提交以下的code提交时请把类名改成"Main",可以直接通过
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.StreamTokenizer;
import java.util.Arrays;
public class Code02_TeamDP {
public static int MAXN = 1001;
public static int MAXM = 1001;
// arr[i][0] 重量
// arr[i][1] 价值
// arr[i][2] 组号
public static int[][] arr = new int[MAXN][3];
public static int[] dp = new int[MAXM];
public static int m, n;
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StreamTokenizer in = new StreamTokenizer(br);
PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out));
while (in.nextToken() != StreamTokenizer.TT_EOF) {
// 总背包的载重
m = (int) in.nval;
in.nextToken();
// 物品数量
n = (int) in.nval;
for (int i = 0; i < n; i++) {
in.nextToken();
arr[i][0] = (int) in.nval;
in.nextToken();
arr[i][1] = (int) in.nval;
in.nextToken();
arr[i][2] = (int) in.nval;
}
// 根据组号排序
// 1 : a b c 2 : d e 3 : f g h
Arrays.sort(arr, 0, n, (a, b) -> a[2] - b[2]);
// dp[位置][剩余重量]
// dp[重量]
Arrays.fill(dp, 0, m + 1, 0);
out.println(compute());
out.flush();
}
}
public static int compute() {
for (int start = 0, end = 1; start < n;) {
// start(首个物品)
// end(当前这个组的越界物品)
// a b c | d e | f g
// 0 1 2 3 4 5 6
while (end < n && arr[end][2] == arr[start][2]) {
end++;
}
// [start...end)是一个组的物品
for (int r = m; r >= 0; r--) {
for (int i = start; i < end; i++) {
if (r >= arr[i][0]) {
dp[r] = Math.max(dp[r], arr[i][1] + dp[r - arr[i][0]]);
}
}
}
start = end++;
}
return dp[m];
}
}

@ -0,0 +1,35 @@
package class_2023_07_4_week;
import java.util.List;
// 一张桌子上总共有 n 个硬币 栈 。每个栈有 正整数 个带面值的硬币
// 每一次操作中,你可以从任意一个栈的 顶部 取出 1 个硬币,从栈中移除它,并放入你的钱包里
// 给你一个列表 piles ,其中 piles[i] 是一个整数数组
// 分别表示第 i 个栈里 从顶到底 的硬币面值。同时给你一个正整数 k
// 请你返回在 恰好 进行 k 次操作的前提下,你钱包里硬币面值之和 最大为多少
// 测试链接 : https://leetcode.cn/problems/maximum-value-of-k-coins-from-piles/
public class Code03_MaximumValueOfKcoinsFromPiles {
public int maxValueOfCoins(List<List<Integer>> piles, int k) {
int[] dp = new int[k + 1];
// 物品总量n 2000内
// 组的数量m 1000内
// 挑选的次数k 2000内
// O( k * n)
// O( m * k^2)
// min ( O( k * n) , O( m * k^2) )
for (List<Integer> stack : piles) { // 组
for (int w = k; w > 0; w--) { // 背包容量
// i = 1 sum = 0
// i = 2 sum = arr[0]
// i = 3 sum = arr[0] + arr[1]
for (int i = 1, sum = 0; i <= Math.min(stack.size(), w); i++) {
sum += stack.get(i - 1);
dp[w] = Math.max(dp[w], sum + dp[w - i]);
}
}
}
return dp[k];
}
}

@ -0,0 +1,132 @@
package class_2023_07_4_week;
// 多维费用背包(概念没用)
// 给你一个二进制字符串数组 strs 和两个整数 m 和 n
// 请你找出并返回 strs 的最大子集的长度,该子集中 最多 有 m 个 0 和 n 个 1
// 如果 x 的所有元素也是 y 的元素,集合 x 是集合 y 的 子集
// 测试链接 : https://leetcode.cn/problems/ones-and-zeroes/
public class Code04_OnesAndZeroes {
public static int findMaxForm1(String[] strs, int m, int n) {
int len = strs.length;
int[][] arr = new int[len][2];
for (int i = 0, zeros, ones; i < strs.length; i++) {
zeros = 0;
ones = 0;
for (int j = 0; j < strs[i].length(); j++) {
if (strs[i].charAt(j) == '0') {
zeros++;
} else {
ones++;
}
}
arr[i][0] = zeros;
arr[i][1] = ones;
}
return process1(arr, 0, m, n);
}
public static int process1(int[][] arr, int i, int z, int o) {
if (i == arr.length) {
return 0;
}
int p1 = process1(arr, i + 1, z, o);
int p2 = 0;
if (arr[i][0] <= z && arr[i][1] <= o) {
p2 = 1 + process1(arr, i + 1, z - arr[i][0], o - arr[i][1]);
}
return Math.max(p1, p2);
}
public static int findMaxForm2(String[] strs, int m, int n) {
int len = strs.length;
int[][] arr = new int[len][2];
for (int i = 0, zeros, ones; i < strs.length; i++) {
zeros = 0;
ones = 0;
for (int j = 0; j < strs[i].length(); j++) {
if (strs[i].charAt(j) == '0') {
zeros++;
} else {
ones++;
}
}
arr[i][0] = zeros;
arr[i][1] = ones;
}
int[][][] dp = new int[len][m + 1][n + 1];
for (int i = 0; i < len; i++) {
for (int j = 0; j <= m; j++) {
for (int k = 0; k <= n; k++) {
dp[i][j][k] = -1;
}
}
}
return process2(arr, 0, m, n, dp);
}
// "101010101" -> 0 4个 1 5个 [4,5]
// "000111" -> [3,3]
public static int process2(int[][] arr, int i, int z, int o, int[][][] dp) {
if (i == arr.length) {
return 0;
}
if (dp[i][z][o] != -1) {
return dp[i][z][o];
}
int p1 = process2(arr, i + 1, z, o, dp);
// 要当前字符串
int p2 = 0;
if (arr[i][0] <= z && arr[i][1] <= o) {
p2 = 1 + process2(arr, i + 1, z - arr[i][0], o - arr[i][1], dp);
}
int ans = Math.max(p1, p2);
dp[i][z][o] = ans;
return ans;
}
public static int findMaxForm3(String[] strs, int m, int n) {
int len = strs.length;
int[][][] dp = new int[len + 1][m + 1][n + 1];
for (int i = len - 1; i >= 0; i--) {
zeroAndOne(strs[i]);
for (int z = 0; z <= m; z++) {
for (int o = 0; o <= n; o++) {
dp[i][z][o] = dp[i + 1][z][o];
if (zeros <= z && ones <= o) {
dp[i][z][o] = Math.max(dp[i][z][o], 1 + dp[i + 1][z - zeros][o - ones]);
}
}
}
}
return dp[0][m][n];
}
public static int zeros, ones;
public static void zeroAndOne(String str) {
zeros = 0;
ones = 0;
for (int i = 0; i < str.length(); i++) {
if (str.charAt(i) == '0') {
zeros++;
} else {
ones++;
}
}
}
public static int findMaxForm4(String[] strs, int m, int n) {
int[][] dp = new int[m + 1][n + 1];
for (String s : strs) {
zeroAndOne(s);
for (int i = m; i >= zeros; i--) {
for (int j = n; j >= ones; j--) {
dp[i][j] = Math.max(dp[i][j], dp[i - zeros][j - ones] + 1);
}
}
}
return dp[m][n];
}
}

@ -0,0 +1,99 @@
package class_2023_07_4_week;
// 集团里有 n 名员工,他们可以完成各种各样的工作创造利润
// 第 i 种工作会产生 profit[i] 的利润,它要求 group[i] 名成员共同参与
// 如果成员参与了其中一项工作,就不能参与另一项工作
// 工作的任何至少产生 minProfit 利润的子集称为 盈利计划
// 并且工作的成员总数最多为 n
// 有多少种计划可以选择?因为答案很大,所以 返回结果模 10^9 + 7 的值。
// 测试链接 : https://leetcode.cn/problems/profitable-schemes/
public class Code05_ProfitableSchemes {
// n : 员工的额度 不能超
// p : 利润的额度 不能少
// group[i] : 需要几个人
// profit[i] : 产生的利润
public static int profitableSchemes1(int n, int minProfit, int[] group, int[] profit) {
return f1(group, profit, 0, n, minProfit);
}
// i : 来到i号项目
// r : 员工还有r人
// s : 利润还有s才能达成目标
// 返回 : 有多少种方案
public static int f1(int[] g, int[] p, int i, int r, int s) {
if (r <= 0) {
// 人已经没了
// s为0或者负数都叫完成工作了
return s <= 0 ? 1 : 0;
}
// r > 0
if (i == g.length) {
// 项目已经没了
// s为0或者负数都叫完成工作了
return s <= 0 ? 1 : 0;
}
int p1 = f1(g, p, i + 1, r, s);
int p2 = 0;
if (g[i] <= r) {
// 100 400 -300 -> 0
p2 = f1(g, p, i + 1, r - g[i], s - p[i]);
}
return p1 + p2;
}
public static int mod = 1000000007;
public static int profitableSchemes2(int n, int minProfit, int[] group, int[] profit) {
int m = group.length;
int[][][] dp = new int[m][n + 1][minProfit + 1];
for (int a = 0; a < m; a++) {
for (int b = 0; b <= n; b++) {
for (int c = 0; c <= minProfit; c++) {
dp[a][b][c] = -1;
}
}
}
return f2(group, profit, 0, n, minProfit, dp);
}
public static int f2(int[] g, int[] p, int i, int r, int s, int[][][] dp) {
if (r <= 0) {
return s == 0 ? 1 : 0;
}
if (i == g.length) {
return s == 0 ? 1 : 0;
}
if (dp[i][r][s] != -1) {
return dp[i][r][s];
}
int p1 = f2(g, p, i + 1, r, s, dp);
int p2 = 0;
if (g[i] <= r) {
p2 = f2(g, p, i + 1, r - g[i], Math.max(0, s - p[i]), dp);
}
int ans = (p1 + p2) % mod;
dp[i][r][s] = ans;
return ans;
}
public static int profitableSchemes3(int n, int minProfit, int[] group, int[] profit) {
int[][] dp = new int[n + 1][minProfit + 1];
for (int r = 0; r <= n; r++) {
dp[r][0] = 1;
}
int m = group.length;
for (int i = m - 1; i >= 0; i--) {
for (int r = n; r >= 0; r--) {
for (int s = minProfit; s >= 0; s--) {
int p1 = dp[r][s];
int p2 = group[i] <= r ? dp[r - group[i]][Math.max(0, s - profit[i])] : 0;
dp[r][s] = (p1 + p2) % mod;
}
}
}
return dp[n][minProfit];
}
}

@ -0,0 +1,207 @@
package class_2023_07_4_week;
// 在一个小城市里,有 m 个房子排成一排
// 你需要给每个房子涂上 n 种颜色之一(颜色编号为 1 到 n )
// 有的房子去年夏天已经涂过颜色了,所以这些房子不可以被重新涂色
// 我们将连续相同颜色尽可能多的房子称为一个街区
// 比方说 houses = [1,2,2,3,3,2,1,1]
// 它包含 5 个街区 [{1}, {2,2}, {3,3}, {2}, {1,1}]
// 给你一个数组 houses ,一个 m * n 的矩阵 cost 和一个整数 target其中
// houses[i]:是第 i 个房子的颜色0 表示这个房子还没有被涂色
// cost[i][j]:是将第 i 个房子涂成颜色 j+1 的花费
// 请你返回房子涂色方案的最小总花费,使得每个房子都被涂色后,恰好组成 target 个街区
// 如果没有可用的涂色方案,请返回 -1
// 测试链接 : https://leetcode.cn/problems/paint-house-iii/
public class Code06_PaintHouseIII {
public static int minCost1(int[] houses, // 每个房子固有的颜色0 :去涂,>0: 不能改的
int[][] cost,// cost[i][j] : i号房子涂上j这个颜色花费多少
int m, // 房子数量
int n, // 颜色数量
int target // 恰好组成 target 个街区
) {
int[][][] dp = new int[m][target + 1][n + 1];
for (int i = 0; i < m; i++) {
for (int k = 0; k <= target; k++) {
for (int c = 0; c <= n; c++) {
dp[i][k][c] = -1;
}
}
}
int ans = process1(houses, cost, n, 0, target, 0, dp);
return ans == Integer.MAX_VALUE ? -1 : ans;
}
// i.... 涂色必须整出k个街区来
// 上一个房子的颜色是c
// 返回最小花费
// 返回Integer.MAX_VALUE做不到
// 返回正常值,代表最小花费
public static int process1(
int[] houses, int[][] cost,
int n, // 颜色总数1 ~ n固定
int i, // 来到的房子编号,可变
int k, // i.... 必须整出k个街区来可变
int c, // 上一个房子的颜色,可变
int[][][] dp) {
if (k < 0) {
return Integer.MAX_VALUE;
}
if (i == houses.length) {
return k == 0 ? 0 : Integer.MAX_VALUE;
}
// k >= 0, 还有房
if (dp[i][k][c] != -1) {
return dp[i][k][c];
}
// 最小花费
int ans = Integer.MAX_VALUE;
if (houses[i] != 0) {
// 不能涂,已经有颜色了
// houses[i] = 3
if (houses[i] != c) {
ans = process1(houses, cost, n, i + 1, k - 1, houses[i], dp);
} else {
ans = process1(houses, cost, n, i + 1, k, houses[i], dp);
}
} else {
// houses[i] == 0
// 能涂
for (int fill = 1, next; fill <= n; fill++) {
// 尝试每一种颜色
if (fill == c) {
next = process1(houses, cost, n, i + 1, k, fill, dp);
} else {
next = process1(houses, cost, n, i + 1, k - 1, fill, dp);
}
if (next != Integer.MAX_VALUE) {
ans = Math.min(ans, cost[i][fill - 1] + next);
}
}
}
dp[i][k][c] = ans;
return ans;
}
public static int minCost2(int[] houses, int[][] cost, int m, int n, int target) {
int[][] dp = new int[target + 1][n + 1];
for (int c = 0; c <= n; c++) {
dp[0][c] = 0;
}
for (int k = 1; k <= target; k++) {
for (int c = 0; c <= n; c++) {
dp[k][c] = Integer.MAX_VALUE;
}
}
int[] memo = new int[n + 1];
for (int i = m - 1; i >= 0; i--) {
if (houses[i] != 0) {
int houseColor = houses[i];
for (int k = target; k >= 0; k--) {
int memory = dp[k][houseColor];
for (int c = 0; c <= n; c++) {
if (houseColor != c) {
dp[k][c] = k == 0 ? Integer.MAX_VALUE : dp[k - 1][houseColor];
} else {
dp[k][c] = memory;
}
}
}
} else {
for (int k = target; k >= 0; k--) {
for (int c = 0; c <= n; c++) {
memo[c] = dp[k][c];
}
for (int c = 0; c <= n; c++) {
int ans = Integer.MAX_VALUE;
for (int fill = 1, next; fill <= n; fill++) {
if (fill == c) {
next = memo[fill];
} else {
next = k == 0 ? Integer.MAX_VALUE : dp[k - 1][fill];
}
if (next != Integer.MAX_VALUE) {
ans = Math.min(ans, cost[i][fill - 1] + next);
}
}
dp[k][c] = ans;
}
}
}
}
return dp[target][0] == Integer.MAX_VALUE ? -1 : dp[target][0];
}
public static int minCost3(int[] houses, int[][] cost, int m, int n, int target) {
int[][] dp = new int[target + 1][n + 1];
for (int c = 0; c <= n; c++) {
dp[0][c] = 0;
}
for (int k = 1; k <= target; k++) {
for (int c = 0; c <= n; c++) {
dp[k][c] = Integer.MAX_VALUE;
}
}
int[] memo = new int[n + 1];
// 0~0 0~1 0~2 0~i
int[] minl = new int[n + 2];
// n ~ n n-1 ~n n-2 ~n i ~n
int[] minr = new int[n + 2];
minl[0] = minr[0] = minl[n + 1] = minr[n + 1] = Integer.MAX_VALUE;
for (int i = m - 1; i >= 0; i--) {
if (houses[i] != 0) {
for (int k = target, memory; k >= 0; k--) {
memory = dp[k][houses[i]];
for (int c = 0; c <= n; c++) {
if (houses[i] != c) {
dp[k][c] = k == 0 ? Integer.MAX_VALUE : dp[k - 1][houses[i]];
} else {
dp[k][c] = memory;
}
}
}
} else {
// O(k)
for (int k = target; k >= 0; k--) {
// O(n)
for (int c = 0; c <= n; c++) {
memo[c] = dp[k][c];
}
// O(n)
for (int fill = 1; fill <= n; fill++) {
if (k == 0 || dp[k - 1][fill] == Integer.MAX_VALUE) {
minl[fill] = minl[fill - 1];
} else {
minl[fill] = Math.min(minl[fill - 1], cost[i][fill - 1] + dp[k - 1][fill]);
}
}
// O(n)
for (int fill = n; fill >= 1; fill--) {
if (k == 0 || dp[k - 1][fill] == Integer.MAX_VALUE) {
minr[fill] = minr[fill + 1];
} else {
minr[fill] = Math.min(minr[fill + 1], cost[i][fill - 1] + dp[k - 1][fill]);
}
}
// O(n)
for (int c = 0, ans; c <= n; c++) {
if (c == 0 || memo[c] == Integer.MAX_VALUE) {
ans = Integer.MAX_VALUE;
} else {
ans = cost[i][c - 1] + memo[c];
}
if (c > 0) {
ans = Math.min(ans, minl[c - 1]);
}
if (c < n) {
ans = Math.min(ans, minr[c + 1]);
}
dp[k][c] = ans;
}
}
}
}
return dp[target][0] != Integer.MAX_VALUE ? dp[target][0] : -1;
}
}

@ -4185,6 +4185,56 @@ int popAtStack(int index) - 返回编号 index 的栈顶部的值,并将其从
第078节 2023年7月4周流行算法题目解析
给你一个正整数数组 nums
每一次操作中,你可以从 nums 中选择 任意 一个数并将它减小到 恰好 一半
注意,在后续操作中你可以对减半过的数继续执行操作
请你返回将 nums 数组和 至少 减少一半的 最少 操作数
测试链接 : https://leetcode.cn/problems/minimum-operations-to-halve-array-sum/
自 01背包问世之后小 A 对此深感兴趣
一天,小 A 去远游,却发现他的背包不同于 01 背包,他的物品大致可分为 k 组
每组中的物品只能选择1件现在他想知道最大的利用价值是多少
测试链接 : www.luogu.com.cn/problem/P1757
一张桌子上总共有 n 个硬币 栈 。每个栈有 正整数 个带面值的硬币
每一次操作中,你可以从任意一个栈的 顶部 取出 1 个硬币,从栈中移除它,并放入你的钱包里
给你一个列表 piles ,其中 piles[i] 是一个整数数组
分别表示第 i 个栈里 从顶到底 的硬币面值。同时给你一个正整数 k
请你返回在 恰好 进行 k 次操作的前提下,你钱包里硬币面值之和 最大为多少
测试链接 : https://leetcode.cn/problems/maximum-value-of-k-coins-from-piles/
给你一个二进制字符串数组 strs 和两个整数 m 和 n
请你找出并返回 strs 的最大子集的长度,该子集中 最多 有 m 个 0 和 n 个 1
如果 x 的所有元素也是 y 的元素,集合 x 是集合 y 的 子集
测试链接 : https://leetcode.cn/problems/ones-and-zeroes/
集团里有 n 名员工,他们可以完成各种各样的工作创造利润
第 i 种工作会产生 profit[i] 的利润,它要求 group[i] 名成员共同参与
如果成员参与了其中一项工作,就不能参与另一项工作
工作的任何至少产生 minProfit 利润的子集称为 盈利计划
并且工作的成员总数最多为 n
有多少种计划可以选择?因为答案很大,所以 返回结果模 10^9 + 7 的值。
测试链接 : https://leetcode.cn/problems/profitable-schemes/
在一个小城市里,有 m 个房子排成一排
你需要给每个房子涂上 n 种颜色之一(颜色编号为 1 到 n )
有的房子去年夏天已经涂过颜色了,所以这些房子不可以被重新涂色
我们将连续相同颜色尽可能多的房子称为一个街区
比方说 houses = [1,2,2,3,3,2,1,1]
它包含 5 个街区 [{1}, {2,2}, {3,3}, {2}, {1,1}]
给你一个数组 houses ,一个 m * n 的矩阵 cost 和一个整数 target其中
houses[i]:是第 i 个房子的颜色0 表示这个房子还没有被涂色
cost[i][j]:是将第 i 个房子涂成颜色 j+1 的花费
请你返回房子涂色方案的最小总花费,使得每个房子都被涂色后,恰好组成 target 个街区
如果没有可用的涂色方案,请返回 -1
测试链接 : https://leetcode.cn/problems/paint-house-iii/

Loading…
Cancel
Save