modify code

master
algorithmzuo 3 years ago
parent 1d3c3ab572
commit e0c47a27e8

@ -131,7 +131,7 @@ public class Code04_MinCoinsOnePaper {
for (int i = N - 1; i >= 0; i--) {
for (int mod = 0; mod < Math.min(aim + 1, c[i]); mod++) {
// 当前面值 X
// mod mod + x mod + 2*x mod + 3 * x
// mod mod + x mod + 2*x mod + 3 * x
LinkedList<Integer> w = new LinkedList<>();
w.add(mod);
dp[i][mod] = dp[i + 1][mod];
@ -145,7 +145,11 @@ public class Code04_MinCoinsOnePaper {
if (w.peekFirst() == overdue) {
w.pollFirst();
}
dp[i][r] = dp[i + 1][w.peekFirst()] + compensate(w.peekFirst(), r, c[i]);
if (dp[i + 1][w.peekFirst()] == Integer.MAX_VALUE) {
dp[i][r] = Integer.MAX_VALUE;
} else {
dp[i][r] = dp[i + 1][w.peekFirst()] + compensate(w.peekFirst(), r, c[i]);
}
}
}
}

@ -0,0 +1,125 @@
package class_2022_11_4_week;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
// 来自国外题目论坛
// 给定一个非负数组arr
// 任何两个数差值的绝对值如果arr中没有都要加入到arr里
// 然后新的arr继续任何两个数差值的绝对值如果arr中没有都要加入到arr里
// 一直到arr大小固定
// 请问最终arr长度是多少
// 1 <= arr的长度 <= 10^5
// 0 <= arr的数值 <= 10^5
public class Code01_AbsToArrayFinalLength {
// 暴力方法
// 为了验证
public static int finalLen1(int[] arr) {
ArrayList<Integer> list = new ArrayList<>();
HashSet<Integer> set = new HashSet<>();
for (int num : arr) {
list.add(num);
set.add(num);
}
while (!finish(list, set))
;
return list.size();
}
public static boolean finish(ArrayList<Integer> list, HashSet<Integer> set) {
int len = list.size();
for (int i = 0; i < len; i++) {
for (int j = i + 1; j < len; j++) {
int abs = Math.abs(list.get(i) - list.get(j));
if (!set.contains(abs)) {
list.add(abs);
set.add(abs);
}
}
}
return len == list.size();
}
// 正式方法
// 时间复杂O(N)
public static int finalLen2(int[] arr) {
int max = 0;
// 任意一个非0的值
int gcd = 0;
for (int num : arr) {
max = Math.max(max, num);
if (num != 0) {
gcd = num;
}
}
if (gcd == 0) { // 数组中都是0
return arr.length;
}
// 不都是0
HashMap<Integer, Integer> counts = new HashMap<>();
for (int num : arr) {
if (num != 0) {
gcd = gcd(gcd, num);
}
counts.put(num, counts.getOrDefault(num, 0) + 1);
}
// max / gcd
int ans = max / gcd;
ans += counts.getOrDefault(0, 0);
boolean add = false;
// 每个数,出现的次数
for (int key : counts.keySet()) {
if (key != 0) {
ans += counts.get(key) - 1;
}
// 3个5 0
// 5 5 2个 0 +1
// 7 7 7 3个
if (!add && counts.get(key) > 1 && !counts.containsKey(0)) {
ans++;
add = true;
}
}
return ans;
}
// O(1)
public static int gcd(int m, int n) {
return n == 0 ? m : gcd(n, m % n);
}
// 为了测试
public static int[] randomArray(int n, int v) {
int[] ans = new int[n];
for (int i = 0; i < n; i++) {
ans[i] = (int) (Math.random() * v);
}
return ans;
}
// 为了测试
public static void main(String[] args) {
int N = 15;
int V = 50;
int testTime = 8000;
System.out.println("功能测试开始");
for (int i = 0; i < testTime; i++) {
int n = (int) (Math.random() * N) + 1;
int[] arr = randomArray(n, V);
int ans1 = finalLen1(arr);
int ans2 = finalLen2(arr);
if (ans1 != ans2) {
for (int num : arr) {
System.out.print(num + " ");
}
System.out.println("出错了!");
}
}
System.out.println("功能测试结束");
}
}

@ -0,0 +1,40 @@
package class_2022_11_4_week;
// 来自字节
// 11.02笔试
// leetcode原题
// 有一堆石头,用整数数组 stones 表示
// 其中 stones[i] 表示第 i 块石头的重量。
// 每一回合,从中选出任意两块石头,然后将它们一起粉碎
// 假设石头的重量分别为 x 和 y且 x <= y
// 那么粉碎的可能结果如下:
// 如果 x == y那么两块石头都会被完全粉碎
// 如果 x != y那么重量为 x 的石头将会完全粉碎,而重量为 y 的石头新重量为 y-x。
// 最后,最多只会剩下一块 石头
// 返回此石头 最小的可能重量
// 如果没有石头剩下,就返回 0
// 测试链接 : https://leetcode.cn/problems/last-stone-weight-ii/
public class Code02_LastStoneWeightII {
public int lastStoneWeightII(int[] arr) {
int n = arr.length;
int sum = 0;
for (int num : arr) {
sum += num;
}
int half = sum / 2;
int[][] dp = new int[n + 1][half + 1];
for (int i = n - 1; i >= 0; i--) {
for (int rest = 0; rest <= half; rest++) {
int p1 = dp[i + 1][rest];
int p2 = 0;
if (arr[i] <= rest) {
p2 = arr[i] + dp[i + 1][rest - arr[i]];
}
dp[i][rest] = Math.max(p1, p2);
}
}
return sum - dp[0][half] - dp[0][half];
}
}

@ -0,0 +1,47 @@
package class_2022_11_4_week;
import java.util.Arrays;
// 给你两个正整数数组 nums 和 target ,两个数组长度相等。
// 在一次操作中,你可以选择两个 不同 的下标 i 和 j
// 其中 0 <= i, j < nums.length ,并且:
// 令 nums[i] = nums[i] + 2 且
// 令 nums[j] = nums[j] - 2 。
// 如果两个数组中每个元素出现的频率相等,我们称两个数组是 相似 的。
// 请你返回将 nums 变得与 target 相似的最少操作次数。
// 测试数据保证 nums 一定能变得与 target 相似。
// 测试链接 : https://leetcode.cn/problems/minimum-number-of-operations-to-make-arrays-similar/
public class Code03_MinOperationsMakeSimilar {
public static long makeSimilar(int[] nums, int[] target) {
int n = nums.length;
int oddSize = split(nums);
split(target);
Arrays.sort(nums, 0, oddSize);
Arrays.sort(nums, oddSize, n);
Arrays.sort(target, 0, oddSize);
Arrays.sort(target, oddSize, n);
long ans = 0;
for (int i = 0; i < n; i++) {
ans += Math.abs((long) nums[i] - target[i]);
}
return ans >> 2;
}
public static int split(int[] arr) {
int line = 0;
for (int i = 0; i < arr.length; i++) {
if ((arr[i] & 1) != 0) {
swap(arr, i, line++);
}
}
return line;
}
public static void swap(int[] arr, int i, int j) {
int tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
}
}

@ -0,0 +1,151 @@
package class_2022_11_4_week;
import java.util.Arrays;
// 给定你一个整数数组 nums
// 我们要将 nums 数组中的每个元素移动到 A 集合 或者 B 集合中
// 使得 A 集合和 B 集合不为空,并且 average(A) == average(B)
// 如果可以完成则返回true否则返回false。
// 注意:对于数组 arr, average(arr) 是 arr 的所有元素的和除以 arr 长度。
// 测试链接 : https://leetcode.cn/problems/split-array-with-same-average/
public class Code04_SplitArrayWithSameAverage {
//
// public static int n;
// public static int sum;
//
// public static boolean can(int[] arr) {
// n = arr.length;
// if (n == 1) {
// return false;
// }
// sum = 0;
// for (int num : arr) {
// sum += num;
// }
//
// int leftSize = n / 2;
// int[] left = new int[leftSize];
// for (int i = 0; i < leftSize; i++) {
// left[i] = arr[i];
// }
//
// int rightSize = n - leftSize;
// int[] right = new int[leftSize];
// for (int i = 0, j = leftSize; j < n; i++, j++) {
// right[i] = arr[j];
// }
//
// HashSet<Integer> ans1 = new HashSet<>();
// collect(left, 0, 0, 0, ans1);
// HashSet<Integer> ans2 = new HashSet<>();
// collect(right, 0, 0, 0, ans2);
//
//
//
// // ans1 所有的指标 : 2 ^ 15 -> 3万
// // ans2 所有的指标 : 2 ^ 15
//
//
//
// for(int x : ans1) { // 2 ^ 15
// // ans2 -x O(1)
// }
//
//
// // left : T1 S1
// // right: T2 S2
// // S1 + S2 / T1 + T2 == Sum / N
// // N * S1 - Sum * T1 == - (N * S2 - Sum * T2)
//
// }
//
// // left[i.....] X Y
// // t, s
// // ans
// public static void collect(int[] part,
// int i, int t, int s, HashSet<Integer> ans) {
// if(i == part.length) {
// ans.add(n * s - sum * t);
// }else {
// collect(part, i+1, t, s, ans);
// collect(part, i+1, t+1, s + part[i], ans);
// }
// }
//
// // 左,
public static int n;
public static int s;
public static int l;
public static int r;
public static int[] lvalues = new int[1 << 15];
// 3000 l = 3000
// 0...2999
public static int[] rvalues = new int[1 << 15];
public static boolean splitArraySameAverage(int[] nums) {
n = nums.length;
if (n == 1) {
return false;
}
s = 0;
for (int num : nums) {
s += num;
}
l = 0;
r = 0;
int[] larr = new int[n / 2];
int[] rarr = new int[n - larr.length];
for (int i = 0; i < larr.length; i++) {
larr[i] = nums[i];
}
for (int i = larr.length, j = 0; i < nums.length; i++, j++) {
rarr[j] = nums[i];
}
// 左侧 : 收集指标的时候,不能一个数也没有
collect(larr, true);
// 右侧 : 收集指标的时候,不能所有数都用
collect(rarr, false);
Arrays.sort(rvalues, 0, r);
for (int i = 0; i < l; i++) {
// 左侧x -x
if (contains(-lvalues[i])) {
return true;
}
}
return false;
}
public static void collect(int[] arr, boolean isLeft) {
process(arr, 0, 0, 0, isLeft);
}
public static void process(int[] arr, int index, int sum, int num, boolean isLeft) {
if (index == arr.length) {
if (isLeft && num > 0) {
lvalues[l++] = s * num - n * sum;
}
if (!isLeft && num != arr.length) {
rvalues[r++] = s * num - n * sum;
}
} else {
process(arr, index + 1, sum, num, isLeft);
process(arr, index + 1, sum + arr[index], num + 1, isLeft);
}
}
public static boolean contains(int num) {
for (int left = 0, right = r - 1, mid = 0; left <= right;) {
mid = (left + right) / 2;
if (rvalues[mid] == num) {
return true;
} else if (rvalues[mid] < num) {
left = mid + 1;
} else {
right = mid - 1;
}
}
return false;
}
}

@ -0,0 +1,191 @@
package class_2022_11_4_week;
import java.util.Arrays;
// 来自第四届全国大学生算法设计与编程挑战赛(秋季赛)
// 给定两个长度为N的数组a[]和b[]
// 也就是对于每个位置i来说有a[i]和b[i]两个属性
// i a[i] b[i]
// j a[j] b[j]
// 现在想为了i选一个最好的j位置搭配能得到最小的如下值:
// (a[i] + a[j]) ^ 2 + b[i] + b[j]
// 我们把这个最小的值定义为i的最in值
// 比如 :
// a = { 2, 3, 6, 5, 1 }
// b = { 100, 70, 20, 40, 150 }
// 0 1 2 3 4
// 0位置和2位置搭配可以得到最in值 : 184
// 1位置和2位置搭配可以得到最in值 : 171
// 2位置和1位置搭配可以得到最in值 : 171
// 3位置和1位置搭配可以得到最in值 : 174
// 4位置和2位置搭配可以得到最in值 : 219
// 注意 : i位置可以和i位置(自己)搭配并不是说i和j一定要是不同的位置
// 返回每个位置i的最in值
// 比如上面的例子,最后返回[184, 171, 171, 174, 219]
// 1 <= N <= 10^5
// 1 <= a[i]、b[i] <= 10^9
public class Code05_PickBestToGetInValue {
// 暴力方法
// 时间复杂度O(N^2)
// 为了测试
public static long[] inValues1(int[] a, int[] b) {
int n = a.length;
long[] ans = new long[n];
for (int i = 0; i < n; i++) {
long curAns = Long.MAX_VALUE;
for (int j = 0; j < n; j++) {
long cur = (long) (a[i] + a[j]) * (a[i] + a[j]) + b[i] + b[j];
curAns = Math.min(curAns, cur);
}
ans[i] = curAns;
}
return ans;
}
// 正式方法
// 时间复杂度O(N*logN)
// (a[i] + a[j]) ^ 2 + b[i] + b[j]
// a[i]^2 + b[i] + 2a[i]a[j] + a[j]^2 + b[j]
// a[i] * ( a[i] + b[i]/a[i] + 2a[j] + (a[j]^2 + b[j])/a[i])
// 令S(j) = 2a[j]
// 令T(j) = a[j]^2 + b[j]
// 那么对于i来说就是选择j让下面得到最小值
// a[i] * ( a[i] + b[i]/a[i] + S(j) + T(j)/a[i])
// 选择最小的S(j) + T(j)/a[i],就得到了答案
// 剩下的一切都是围绕这个
public static long[] inValues2(int[] a, int[] b) {
int n = a.length;
// i a[i] b[i]
// i s[i] t[i]
long[][] st = new long[n][2];
for (int i = 0; i < n; i++) {
st[i][0] = 2L * a[i];
st[i][1] = (long) a[i] * a[i] + b[i];
}
// 只需要根据S值从大到小排序即可
// 下面的比较器定义稍复杂因为java里排序会严格检查传递性
// 所以策略参考了S和T其实只需要根据S值从大到小排序即可
Arrays.sort(st, (x, y) -> x[0] != y[0] ? (x[0] > y[0] ? -1 : 1) : (x[1] <= y[1] ? -1 : 1));
int[] stack = new int[n];
int r = 0;
for (int i = 0; i < n; i++) {
// s 大 -> 小
long s = st[i][0];
long t = st[i][1];
while (r > 0
&& tail(st, stack, r) >= // 栈顶,和栈顶再下一个 ai大到什么程度栈顶更好!
// 当前记录当ai大到什么程度比栈顶好
better(st[stack[r - 1]][0], st[stack[r - 1]][1], s, t)) {
r--;
}
stack[r++] = i;
}
int[][] arr = new int[n][3];
for (int i = 0; i < n; i++) {
arr[i][0] = i;
arr[i][1] = a[i];
arr[i][2] = b[i];
}
// 只需要根据a值从大到小排序即可
// 下面的比较器定义稍复杂排序策略参考了a和位置i原因是:
// 因为java的系统排序会检查传递性
// 所以只用a排序会导致java内部认为这个排序是无效的
// 其实只需要根据a值从大到小排序即可
Arrays.sort(arr, (x, y) -> x[1] != y[1] ? (x[1] < y[1] ? 1 : -1) : (x[0] - y[0]));
long[] ans = new long[n];
for (int k = 0; k < n; k++) {
int i = arr[k][0];
int ai = arr[k][1];
int bi = arr[k][2];
while (tail(st, stack, r) > ai) {
r--;
}
long sj = st[stack[r - 1]][0];
long tj = st[stack[r - 1]][1];
// a[i] * ( a[i] + b[i]/a[i] + S(j) + T(j)/a[i])
long curAns = sj * ai + tj + (long) ai * ai + bi;
ans[i] = curAns;
}
return ans;
}
public static int tail(long[][] st, int[] deque, int r) {
if (r == 1) {
return 1;
}
return better(st[deque[r - 2]][0], st[deque[r - 2]][1], st[deque[r - 1]][0], st[deque[r - 1]][1]);
}
// 入参时候s1>=s2这是一定的
// 返回当ai大到什么值的时候(s2+t2/ai) <= (s1+t1/ai)
// 即 : ai大到什么值的时候后者更好
public static int better(long s1, long t1, long s2, long t2) {
if (s1 == s2) {
return t1 <= t2 ? Integer.MAX_VALUE : 1;
}
// s1 > s2
if (t1 >= t2) {
return 1;
}
// s1 > s2
// t1 < t2
long td = t2 - t1;
long sd = s1 - s2;
return (int) ((td + sd - 1) / sd);
}
// 为了测试
public static int[] randomArray(int n, int v) {
int[] ans = new int[n];
for (int i = 0; i < n; i++) {
ans[i] = (int) (Math.random() * v) + 1;
}
return ans;
}
// 为了测试
public static boolean isSameArray(long[] arr1, long[] arr2) {
for (int i = 0; i < arr1.length; i++) {
if (arr1[i] != arr2[i]) {
return false;
}
}
return true;
}
// 为了测试
public static void main(String[] args) {
int N = 100;
int A = 100;
int B = 50000;
int testTime = 50000;
System.out.println("功能测试开始");
for (int i = 0; i < testTime; i++) {
int n = (int) (Math.random() * N) + 1;
int[] a = randomArray(n, A);
int[] b = randomArray(n, B);
long[] ans1 = inValues1(a, b);
long[] ans2 = inValues2(a, b);
if (!isSameArray(ans1, ans2)) {
System.out.println("出错了!");
}
}
System.out.println("功能测试结束");
System.out.println("性能测试开始");
int n = 100000;
int v = 1000000000;
int[] a = randomArray(n, v);
int[] b = randomArray(n, v);
System.out.println("数组长度 : " + n);
System.out.println("数值范围 : " + v);
long start = System.currentTimeMillis();
inValues2(a, b);
long end = System.currentTimeMillis();
System.out.println("运行时间 : " + (end - start) + " 毫秒");
System.out.println("性能测试结束");
}
}

@ -2464,7 +2464,69 @@ b.hello()座位5、座位6都是离最近人的距离最远的(3)
第049节 2022年11月第4周流行算法题目解析
来自国外题目论坛
给定一个非负数组arr
任何两个数差值的绝对值如果arr中没有都要加入到arr里
然后新的arr继续任何两个数差值的绝对值如果arr中没有都要加入到arr里
一直到arr大小固定
请问最终arr长度是多少
1 <= arr的长度 <= 10^5
0 <= arr的数值 <= 10^5
来自字节
11.02笔试
leetcode原题
有一堆石头,用整数数组 stones 表示
其中 stones[i] 表示第 i 块石头的重量。
每一回合,从中选出任意两块石头,然后将它们一起粉碎
假设石头的重量分别为 x 和 y且 x <= y
那么粉碎的可能结果如下:
如果 x == y那么两块石头都会被完全粉碎
如果 x != y那么重量为 x 的石头将会完全粉碎,而重量为 y 的石头新重量为 y-x。
最后,最多只会剩下一块 石头
返回此石头 最小的可能重量
如果没有石头剩下,就返回 0
测试链接 : https://leetcode.cn/problems/last-stone-weight-ii/
给你两个正整数数组 nums 和 target ,两个数组长度相等。
在一次操作中,你可以选择两个 不同 的下标 i 和 j
其中 0 <= i, j < nums.length ,并且:
令 nums[i] = nums[i] + 2 且
令 nums[j] = nums[j] - 2 。
如果两个数组中每个元素出现的频率相等,我们称两个数组是 相似 的。
请你返回将 nums 变得与 target 相似的最少操作次数。
测试数据保证 nums 一定能变得与 target 相似。
测试链接 : https://leetcode.cn/problems/minimum-number-of-operations-to-make-arrays-similar/
给定你一个整数数组 nums
我们要将 nums 数组中的每个元素移动到 A 集合 或者 B 集合中
使得 A 集合和 B 集合不为空,并且 average(A) == average(B)
如果可以完成则返回true否则返回false。
注意:对于数组 arr, average(arr) 是 arr 的所有元素的和除以 arr 长度。
测试链接 : https://leetcode.cn/problems/split-array-with-same-average/
来自第四届全国大学生算法设计与编程挑战赛(秋季赛)
给定两个长度为N的数组a[]和b[]
也就是对于每个位置i来说有a[i]和b[i]两个属性
现在想为了i选一个最好的j位置搭配能得到最小的如下值:
(a[i] + a[j]) ^ 2 + b[i] + b[j]
我们把这个最小的值定义为i的最in值
比如 :
a = { 2, 3, 6, 5, 1 }
b = { 100, 70, 20, 40, 150 }
0 1 2 3 4
0位置和2位置搭配可以得到最in值 : 184
1位置和2位置搭配可以得到最in值 : 171
2位置和1位置搭配可以得到最in值 : 171
3位置和1位置搭配可以得到最in值 : 174
4位置和2位置搭配可以得到最in值 : 219
注意 : i位置可以和i位置(自己)搭配并不是说i和j一定要是不同的位置
返回每个位置i的最in值
比如上面的例子,最后返回[184, 171, 171, 174, 219]
1 <= N <= 10^5
1 <= a[i]、b[i] <= 10^9

Loading…
Cancel
Save