modify code

master
algorithmzuo 3 years ago
parent 25363e2867
commit 40d65b2dc6

@ -0,0 +1,191 @@
package class_2022_11_1_week;
import java.util.Arrays;
import java.util.TreeSet;
// 来自华为
// 做甜点需要购买配料目前共有n种基料和m种配料可供选购
// 制作甜点需要遵循以下几条规则:
// 必须选择1种基料可以添加0种、1种或多种配料每种类型的配料最多添加2份
// 给定长度为n的数组base, base[i]表示第i种基料的价格
// 给定长度为m的数组topping, topping[j]表示第j种配料的价格
// 给定一个正数target表示你做的甜点最终的价格要尽量接近这个数值
// 返回最接近这个数值的价格是多少
// 如果有多个方案都最接近target返回价格最小的那个答案
// 1 <= n,m <= 10
// 1 <= base[i], topping[j] <= 10 ^ 4
// 1 <= target <= 10 ^ 4
public class Code01_DessertPriceClosedTarget {
// 方法1用有序表的方法
public static int closedTarget1(int[] base, int[] topping, int target) {
// 辅料所能产生的所有价格!
// 0 5 15 23
TreeSet<Integer> set = new TreeSet<>();
// 暴力展开!收集所有能产生的价格!放入辅料表里去!
process1(topping, 0, 0, set);
int ans = Integer.MAX_VALUE;
for (int num : base) {
// 枚举每一种主料的价格!
// 最终能搭配出来的最接近的价格
int cur = num;
// 20 100
// 110 100
if (num < target) { // cur < 要求
// 60 100
// 40
int rest = target - num;
// <= rest 最接近的!
Integer floor = set.floor(rest);
// >= rest 最接近的!
Integer ceiling = set.ceiling(rest);
if (floor == null || ceiling == null) {
cur += floor == null ? ceiling : floor;
} else {
cur += rest - floor <= ceiling - rest ? floor : ceiling;
}
// cur会选择floor,或ceiling谁加上最接近target选谁
}
if (Math.abs(cur - target) < Math.abs(ans - target)
|| (Math.abs(cur - target) == Math.abs(ans - target) && cur < ans)) {
ans = cur;
}
}
return ans;
}
// 暴力展开!收集所有能产生的价格!放入辅料表里去!
// topping[index....]
// topping[0...index-1] sum
public static void process1(int[] topping, int index, int sum, TreeSet<Integer> set) {
if (index == topping.length) {
set.add(sum);
} else {
process1(topping, index + 1, sum, set);
process1(topping, index + 1, sum + topping[index], set);
process1(topping, index + 1, sum + (topping[index] << 1), set);
}
}
// 方法2用数组排序+二分的方法
public static int[] collect = new int[14348907];
public static int size = 0;
public static int closedTarget2(int[] base, int[] topping, int target) {
size = 0;
process2(topping, 0, 0);
Arrays.sort(collect, 0, size);
int ans = Integer.MAX_VALUE;
for (int num : base) {
int cur = num;
if (num < target) {
int rest = target - num;
int floor = floor(rest);
int ceiling = ceiling(rest);
if (floor == -1 || ceiling == -1) {
cur += floor == -1 ? ceiling : floor;
} else {
cur += rest - floor <= ceiling - rest ? floor : ceiling;
}
}
if (Math.abs(cur - target) < Math.abs(ans - target)
|| (Math.abs(cur - target) == Math.abs(ans - target) && cur < ans)) {
ans = cur;
}
}
return ans;
}
public static void process2(int[] topping, int index, int sum) {
if (index == topping.length) {
collect[size++] = sum;
} else {
process2(topping, index + 1, sum);
process2(topping, index + 1, sum + topping[index]);
process2(topping, index + 1, sum + (topping[index] << 1));
}
}
public static int floor(int num) {
int l = 0;
int r = size - 1;
int m = 0;
int ans = -1;
while (l <= r) {
m = (l + r) / 2;
if (collect[m] <= num) {
ans = collect[m];
l = m + 1;
} else {
r = m - 1;
}
}
return ans;
}
public static int ceiling(int num) {
int l = 0;
int r = size - 1;
int m = 0;
int ans = -1;
while (l <= r) {
m = (l + r) / 2;
if (collect[m] >= num) {
ans = collect[m];
r = m - 1;
} else {
l = m + 1;
}
}
return ans;
}
// 为了验证
public static int[] randomArray(int n, int v) {
int[] arr = new int[n];
for (int i = 0; i < n; i++) {
arr[i] = (int) (Math.random() * v) + 1;
}
return arr;
}
// 为了验证
public static void main(String[] args) {
int N = 8;
int V = 10000;
int testTime = 5000;
System.out.println("功能测试开始");
for (int i = 0; i < testTime; i++) {
int n = (int) (Math.random() * N) + 1;
int m = (int) (Math.random() * N) + 1;
int[] base = randomArray(n, V);
int[] topping = randomArray(m, V);
int target = (int) (Math.random() * V) + 1;
int ans1 = closedTarget1(base, topping, target);
int ans2 = closedTarget2(base, topping, target);
if (ans1 != ans2) {
System.out.println("出错了!");
}
}
System.out.println("功能测试结束");
System.out.println("性能测试开始");
int n = 15;
int m = 15;
int[] base = randomArray(n, V);
int[] topping = randomArray(m, V);
int target = (int) (Math.random() * V) + 1;
System.out.println("base数组长度 : " + n);
System.out.println("topping数组长度 : " + m);
System.out.println("数值范围 : " + V);
long start = System.currentTimeMillis();
closedTarget2(base, topping, target);
long end = System.currentTimeMillis();
System.out.println("运行时间 : " + (end - start) + " 毫秒");
System.out.println("性能测试结束");
}
}

@ -0,0 +1,78 @@
package class_2022_11_1_week;
// 来自蚂蚁金服
// 得分的定义 :
// 含有大小2*2的矩阵要么
// 1 0
// 0 1 可以得1分
// 要么
// 0 1
// 1 0 可以得1分
// 那么一个任意大小的矩阵就有若干得分点,比如
// 0 1 0
// 1 0 1
// 这个矩阵就有2个得分点
// 给定正数N正数M求所有可能的情况里所有的得分点总和
// 1 <= N、M <= 10^9
public class Code02_ScoreAllMatrix {
public static int score1(int n, int m) {
if (n < 2 || m < 2) {
return 0;
}
int[][] matrix = new int[n][m];
return process(matrix, 0, 0, n, m);
}
public static int process(int[][] matrix, int i, int j, int n, int m) {
if (i == n) {
int score = 0;
for (int r = 1; r < n; r++) {
for (int c = 1; c < m; c++) {
if (check(matrix, r, c)) {
score++;
}
}
}
return score;
}
if (j == m) {
return process(matrix, i + 1, 0, n, m);
}
int score = 0;
matrix[i][j] = 1;
score += process(matrix, i, j + 1, n, m);
matrix[i][j] = 0;
score += process(matrix, i, j + 1, n, m);
return score;
}
public static boolean check(int[][] m, int r, int c) {
return (m[r - 1][c - 1] == 0 && m[r][c - 1] == 1 && m[r - 1][c] == 1 && m[r][c] == 0)
|| (m[r - 1][c - 1] == 1 && m[r][c - 1] == 0 && m[r - 1][c] == 0 && m[r][c] == 1);
}
public static int score2(int n, int m) {
if (n < 2 || m < 2) {
return 0;
}
// n <= 10^9
// m <= 10^9
// 取mod
// (n * m - m - n + 1) -> O(1)
// 2^(n * m - 3) ???
// 真实的笔试场景下:
// 算2^(k)次方的
// 体系学习班章节27学习快速幂
// 本代码不处理mod
return (n * m - m - n + 1) * (1 << (n * m - 3));
}
public static void main(String[] args) {
int n = 3;
int m = 4;
System.out.println(score1(n, m));
System.out.println(score2(n, m));
}
}

@ -0,0 +1,125 @@
package class_2022_11_1_week;
import java.util.Arrays;
// 来自蚂蚁金服
// 小红有n个朋友, 她准备开个宴会,邀请一些朋友
// i号朋友的愉悦值为a[i]财富值为b[i]
// 如果两个朋友同时参加宴会,这两个朋友之间的隔阂是其财富值差值的绝对值
// 宴会的隔阂值,是财富差距最大的两人产生的财富值差值的绝对值
// 宴会的愉悦值,是所有参加宴会朋友的愉悦值总和
// 小红可以邀请任何人,
// 希望宴会的愉悦值不能小于k的情况下 宴会的隔阂值能最小是多少
// 如果做不到,返回-1
// 1 <= n <= 2 * 10^5
// 1 <= 愉悦值、财富值 <= 10^9
// 1 <= k <= 10^14
public class Code03_HappyLimitLessGap {
// 暴力方法
// 为了验证
public static int lessGap1(int[] a, int[] b, int k) {
long ans = process(a, b, 0, k, Integer.MAX_VALUE, Integer.MIN_VALUE);
return ans < Integer.MAX_VALUE ? (int) ans : -1;
}
// 暴力方法
// 为了验证
public static long process(int[] a, int[] b, int i, int rest, int min, int max) {
if (rest <= 0) {
return (long) max - (long) min;
}
if (i == a.length) {
return (long) Integer.MAX_VALUE;
}
long p1 = process(a, b, i + 1, rest, min, max);
long p2 = process(a, b, i + 1, rest - a[i], Math.min(min, b[i]), Math.max(max, b[i]));
return Math.min(p1, p2);
}
// 正式方法
// 二分答案
public static int lessGap2(int[] a, int[] b, long k) {
int n = a.length;
// a : 20 30 17
// b : 5 10 36
// 0 1 2
// [ 20, 5] [30, 10] [17, 36]
// 0 1 2
int[][] f = new int[n][2];
int min = b[0];
int max = b[0];
for (int i = 0; i < n; i++) {
f[i][0] = a[i];
f[i][1] = b[i];
min = Math.min(min, b[i]);
max = Math.max(max, b[i]);
}
// 排序和大流程,没关系
// 是子函数maxHappy函数需要用到排了序
// 根据财富排序,少 -> 多
Arrays.sort(f, (x, y) -> x[1] - y[1]);
// 隔阂值的范围 l ~ r
int l = 0;
int r = max - min;
int m = 0;
int ans = -1;
while (l <= r) {
// 0.........50
// 25
m = (l + r) / 2;
if (maxHappy(f, m) >= k) {
ans = m;
r = m - 1;
} else {
l = m + 1;
}
}
return ans;
}
public static long maxHappy(int[][] f, int limit) {
int n = f.length;
long sum = 0;
long ans = 0;
for (int l = 0, r = 0; l < n; l++) {
while (r < n && f[r][1] - f[l][1] <= limit) {
sum += f[r++][0];
}
ans = Math.max(ans, sum);
sum -= f[l][0];
r = Math.max(r, l + 1);
}
return ans;
}
// 为了验证
public static int[] randomArray(int n, int v) {
int[] arr = new int[n];
for (int i = 0; i < n; i++) {
arr[i] = (int) (Math.random() * v) + 1;
}
return arr;
}
// 为了验证
public static void main(String[] args) {
int N = 15;
int V = 20;
int testTime = 5000;
System.out.println("功能测试开始");
for (int i = 0; i < testTime; i++) {
int n = (int) (Math.random() * N) + 1;
int[] a = randomArray(n, V);
int[] b = randomArray(n, V);
int k = (int) (Math.random() * n * V) + 1;
int ans1 = lessGap1(a, b, k);
int ans2 = lessGap2(a, b, k);
if (ans1 != ans2) {
System.out.println("出错了!");
}
}
System.out.println("功能测试结束");
}
}

@ -0,0 +1,120 @@
package class_2022_11_1_week;
import java.util.Arrays;
// 来自CISCO
// 给定两个正整数x、y都是int整型(java里)
// 返回0 ~ x以内每位数字加起来是y的数字个数
// 比如x = 20、y = 5返回2
// 因为0 ~ x以内每位数字加起来是5的数字有5、14
// x、y范围是java里正整数的范围
// x <= 2 * 10^9
// y <= 90
public class Code04_ValueNoMoreXDigitsToY {
// 暴力方法
// 为了测试
public static int num1(int x, int y) {
int ans = 0;
for (int i = 0; i <= x; i++) {
if (check1(i, y)) {
ans++;
}
}
return ans;
}
public static boolean check1(int num, int y) {
int sum = 0;
while (num != 0) {
sum += num % 10;
num /= 10;
}
return sum == y;
}
// 正式方法
// 时间复杂度O(整数有几个十进制位的平方 * 9)
public static int num2(int x, int y) {
if (x < 0 || y > 90) {
return 0;
}
if (x == 0) {
return y == 0 ? 1 : 0;
}
// x : 352764
// len : 6
// offset : 100000
int offset = 1;
int len = 1;
while (offset <= x / 10) {
offset *= 10;
len++;
}
int[][] dp = new int[len + 1][y + 1];
for (int i = 0; i <= len; i++) {
Arrays.fill(dp[i], -1);
}
return count(x, offset, len, y, dp);
}
// x, 0~x 范围,固定的!
// abcdef
// offset, 100000, 当前关心的是哪位数字
// 10000
// 1000
// len, 还剩几位数字没处理完!
// rest, 每位数字的和加起来,要求是多少
// 0~x ,在之前决定的数字不管,还剩下的数字能变出多少达标的!
public static int count(int x, int offset, int len, int rest, int[][] dp) {
if (len == 0) {
return rest == 0 ? 1 : 0;
}
if (dp[len][rest] != -1) {
return dp[len][rest];
}
int ans = 0;
// x = 3457
// 3
// cur = 3
// 0 ? len-1 rest - 0
// 1 ? len-1 rest - 1
// 2 ? len-1 rest - 2
int cur = (x / offset) % 10;
for (int i = 0; i < cur && i <= rest; i++) {
ans += form[len - 1][rest - i];
}
// cur = 3 2
// 3 5
if (cur <= rest) { // cur!
ans += count(x, offset / 10, len - 1, rest - cur, dp);
}
dp[len][rest] = ans;
return ans;
}
// 打表了
// from[i][j] : 一共有i位所有的数字中每一位加起来是j的数有几个
public static int[][] form = new int[11][91];
static {
form[0][0] = 1;
for (int len = 1; len <= 10; len++) {
for (int sum = 0; sum <= len * 9; sum++) {
for (int cur = 0; cur <= 9 && cur <= sum; cur++) {
form[len][sum] += form[len - 1][sum - cur];
}
}
}
}
// 为了测试
public static void main(String[] args) {
System.out.println(Integer.MAX_VALUE);
int x = 88739128;
int y = 37;
System.out.println(num1(x, y));
System.out.println(num2(x, y));
}
}

@ -0,0 +1,70 @@
package class_2022_11_1_week;
// 给你下标从 0 开始、长度为 n 的字符串 pattern 
// 它包含两种字符,'I' 表示 上升 ,'D' 表示 下降 。
// 你需要构造一个下标从 0 开始长度为 n + 1 的字符串且它要满足以下条件
// num 包含数字 '1' 到 '9' ,其中每个数字 至多 使用一次。
// 如果 pattern[i] == 'I' 那么 num[i] < num[i + 1] 。
// 如果 pattern[i] == 'D' 那么 num[i] > num[i + 1] 。
// 请你返回满足上述条件字典序 最小 的字符串 num。
// 测试链接 : https://leetcode.cn/problems/construct-smallest-number-from-di-string/
public class Code05_CreateMinNumberFromPattern {
public static String smallestNumber(String pattern) {
return String.valueOf(create(pattern.toCharArray(), 0, 0, 0));
}
// pattern I I I D
// 0 1 2 i
// 1 3 4 5 2
// -1
// 1589
// 9 8 5 4 3 2 1 0
// 1 1 1 0 0 0 1 0
// number = 1589
// 返回 i... 所有数字都决定了并且不破坏pattern并且1~9每个数字最多用一次
// 能出来的最小值是啥,返回
public static int create(char[] pattern, int index, int status, int number) {
if (index == pattern.length + 1) {
return number;
}
int cur = 0;
while ((cur = next(status, cur)) != -1) {
// cur == 0 , 当前位1 X
// cur == 1 , 当前位2 X
// cur == 2, 当前位4
// partern I D >
// 0 1 2 3
// ? ? ? ?
// D
// 0 1
// 5 ?
if (index == 0
||
(pattern[index - 1] == 'I' && number % 10 < cur) || (pattern[index - 1] == 'D' && number % 10 > cur)) {
int ans = create(pattern, index + 1, status | (1 << cur), number * 10 + cur);
if (ans != -1) {
return ans;
}
}
}
return -1;
}
// status :
// 9 8 7 6 5 4 3 2 1 0
// 1 1 1 1 1 1 0 0 1 0
// 返回没有使用,且 > num, 最小的数字
// num = 3
public static int next(int status, int num) {
for (int i = num + 1; i <= 9; i++) {
if ((status & (1 << i)) == 0) {
return i;
}
}
return -1;
}
}

@ -2238,7 +2238,7 @@ n <= 3 * 10^5
第046节 2022年10月第3周流行算法题目解析
第046节 2022年10月第4周流行算法题目解析
来自华为
若两个正整数的和为素数,则这两个正整数称之为"素数伴侣"
@ -2326,15 +2326,66 @@ n <= 3 * 10^5
第047节 2022年11月第1周流行算法题目解析
来自华为
做甜点需要购买配料目前共有n种基料和m种配料可供选购
制作甜点需要遵循以下几条规则:
必须选择1种基料可以添加0种、1种或多种配料每种类型的配料最多添加2份
给定长度为n的数组base, base[i]表示第i种基料的价格
给定长度为m的数组topping, topping[j]表示第j种配料的价格
给定一个正数target表示你做的甜点最终的价格要尽量接近这个数值
返回最接近这个数值的价格是多少
如果有多个方案都最接近target返回价格最小的那个答案
1 <= n,m <= 10
1 <= base[i], topping[j] <= 10 ^ 4
1 <= target <= 10 ^ 4
来自蚂蚁金服
得分的定义 :
含有大小2*2的矩阵要么
1 0
0 1 可以得1分
要么
0 1
1 0 可以得1分
那么一个任意大小的矩阵就有若干得分点,比如
0 1 0
1 0 1
这个矩阵就有2个得分点
给定正数N正数M求所有可能的情况里所有的得分点总和
1 <= N、M <= 10^9
来自蚂蚁金服
小红有n个朋友, 她准备开个宴会,邀请一些朋友
i号朋友的愉悦值为a[i]财富值为b[i]
如果两个朋友同时参加宴会,这两个朋友之间的隔阂是其财富值差值的绝对值
宴会的隔阂值,是财富差距最大的两人产生的财富值差值的绝对值
宴会的愉悦值,是所有参加宴会朋友的愉悦值总和
小红可以邀请任何人,
希望宴会的愉悦值不能小于k的情况下 宴会的隔阂值能最小是多少
如果做不到,返回-1
1 <= n <= 2 * 10^5
1 <= 愉悦值、财富值 <= 10^9
1 <= k <= 10^14
来自CISCO
给定两个正整数x、y都是int整型(java里)
返回0 ~ x以内每位数字加起来是y的数字个数
比如x = 20、y = 5返回2
因为0 ~ x以内每位数字加起来是5的数字有5、14
x、y范围是java里正整数的范围
x <= 2 * 10^9
y <= 90
给你下标从 0 开始、长度为 n 的字符串 pattern 
它包含两种字符,'I' 表示 上升 ,'D' 表示 下降 。
你需要构造一个下标从 0 开始长度为 n + 1 的字符串且它要满足以下条件
num 包含数字 '1' 到 '9' ,其中每个数字 至多 使用一次。
如果 pattern[i] == 'I' 那么 num[i] < num[i + 1] 。
如果 pattern[i] == 'D' 那么 num[i] > num[i + 1] 。
请你返回满足上述条件字典序 最小 的字符串 num。
测试链接 : https://leetcode.cn/problems/construct-smallest-number-from-di-string/

Loading…
Cancel
Save