modify code

master
algorithmzuo 2 years ago
parent cbb91cf4ec
commit ada737c9c0

@ -0,0 +1,51 @@
package class_2023_05_4_week;
import java.util.Arrays;
import java.util.HashMap;
// 先来一个智力题,来自美团面试题
// 给定n个二维坐标表示在二维平面的n个点
// 坐标为double类型精度最多小数点后两位
// 希望在二维平面上画一个圆圈住其中的k个点其他的n-k个点都要在圆外
// 返回一个圆心和半径表示哪个圆可以圈住其中的k个点
// 坐标和半径都是double类型最多保留小数点后两位
// 下面是正式题目
// 给你一个整数数组 arr 和一个整数 k
// 现需要从数组中恰好移除 k 个元素
// 请找出移除后数组中不同整数的最少数目
// 测试链接 : https://leetcode.cn/problems/least-number-of-unique-integers-after-k-removals/
public class Code01_LeastNumberOfUniqueAfterRemovals {
public static int findLeastNumOfUniqueInts(int[] arr, int k) {
HashMap<Integer, Integer> map = new HashMap<>();
for (int num : arr) {
map.put(num, map.getOrDefault(num, 0) + 1);
}
// 2 : 5次
// 4 : 9次
// 7 : 2次
// 5 : 6次
int n = map.size();
int[] cnts = new int[n];
int i = 0;
for (int cnt : map.values()) {
cnts[i++] = cnt;
}
// [5, 9, 2, 6]
// [2, 5, 6, 9]
Arrays.sort(cnts);
for (i = 0; i < n; i++) {
k -= cnts[i];
if (k <= 0) {
// 该结束了
if (k == 0) {
// i位置词频彻底耗费完了
i++;
}
break;
}
}
return n - i;
}
}

@ -0,0 +1,96 @@
package class_2023_05_4_week;
// 来自华为
// 一个数字n一定要分成k份
// 得到的乘积尽量大是多少
// 数字n和k可能非常大到达10^12规模
// 结果可能更大所以返回结果对1000000007取模
public class Code02_SplitNtoKMaxProduct {
// 暴力递归
// 一定能得到最优解
public static int maxValue1(int n, int k) {
if (k == 0 || n < k) {
return -1;
}
return process1(n, k);
}
// 剩余的数字rest一定要拆成j份返回最大乘积
public static int process1(int rest, int j) {
if (j == 1) {
return rest;
}
// 10 , 3份
// 1 * f(9,2)
// 2 * f(8,2)
// 3 * f(7,2)
// ...
int ans = Integer.MIN_VALUE;
for (int cur = 1; cur <= rest && (rest - cur) >= (j - 1); cur++) {
int curAns = cur * process1(rest - cur, j - 1);
ans = Math.max(ans, curAns);
}
return ans;
}
// 贪心的解
// 这不是最优解,只是展示贪心思路
public static int maxValue2(int n, int k) {
if (k == 0 || n < k) {
return -1;
}
// 数字n一定要分k份
// 每份先得多少n/k
int a = n / k;
// 有多少份可以升级成a+1
int b = n % k;
int ans = 1;
for (int i = 0; i < b; i++) {
ans *= a + 1;
}
for (int i = 0; i < k - b; i++) {
ans *= a;
}
return ans;
}
// 贪心的解
// 这是最优解
// 但是如果结果很大,让求余数...
public static int maxValue3(long n, long k) {
if (k == 0 || n < k) {
return -1;
}
int mod = 1000000007;
long a = n / k;
long b = n % k;
long part1 = power(a + 1, b, mod);
long part2 = power(a, k - b, mod);
return (int) (part1 * part2) % mod;
}
// 返回a的n次方%mod的结果
public static long power(long a, long n, int mod) {
long ans = 1;
long tmp = a;
while (n != 0) {
if ((n & 1) != 0) {
ans = (ans * tmp) % mod;
}
n >>= 1;
tmp = (tmp * tmp) % mod;
}
return ans;
}
public static void main(String[] args) {
// 可以自己来用参数实验
int n = 20;
int k = 4;
System.out.println(maxValue1(n, k));
System.out.println(maxValue2(n, k));
// System.out.println(maxValue3(n, k));
}
}

@ -0,0 +1,25 @@
package class_2023_05_4_week;
// 来自美团
// 给定 pushed 和 popped 两个序列,每个序列中的 值都不重复,
// 只有当它们可能是在最初空栈上进行的推入 push 和弹出 pop 操作序列的结果时,
// 返回 true否则返回 false 。
// 测试链接 : https://leetcode.cn/problems/validate-stack-sequences/
public class Code03_ValidateStackSequences {
public boolean validateStackSequences(int[] pushed, int[] popped) {
int n = pushed.length;
int size = 0;
for (int i = 0, j = 0; i < n; i++) {
// i : 入栈数组,哪个位置的数要进栈
// j : 出栈数组,对比的位置
pushed[size++] = pushed[i];
while (size > 0 && j < n && pushed[size - 1] == popped[j]) {
size--;
j++;
}
}
return size == 0;
}
}

@ -0,0 +1,83 @@
package class_2023_05_4_week;
import java.util.ArrayList;
import java.util.PriorityQueue;
// 来自招商银行
// 给定一个数组arr长度为n表示有0~n-1号设备
// arr[i]表示i号设备的型号型号的种类从0~k-1一共k种型号
// 给定一个k*k的矩阵map来表示型号之间的兼容情况
// map[a][b] == 1表示a型号兼容b型号
// map[a][b] == 0表示a型号不兼容b型号
// 兼容关系是有向图也就是a型号兼容b型号不代表b型号同时兼容a型号
// 如果i设备的型号兼容j设备的型号那么可以从i设备修建一条去往j设备的线路
// 修建线路的代价是i设备到j设备的距离|i-j|
// 你的目标是从0号设备到达n-1号设备并不一定每个设备都联通只需要到达即可
// 返回最小的修建代价,如果就是无法到达返回-1
// 1 <= n <= 1000
// 1 <= k <= 50
public class Code04_EquipmentFirstToEnd {
public static int minCost(int[] arr, int[][] map, int n, int k) {
// 0 : {4,7,13,26}
// 1 : {5,45,3,17}
ArrayList<ArrayList<Integer>> own = new ArrayList<>();
// 0 1 2 3
// 0 1 0 1 0
// 1 0 1 1 0
// 2 1 1 1 1
// 3
// 0 : 0, 2
// 1 : 1, 2
// 2 : 0 1 2 3
ArrayList<ArrayList<Integer>> nexts = new ArrayList<>();
for (int i = 0; i < k; i++) {
own.add(new ArrayList<>());
nexts.add(new ArrayList<>());
}
for (int i = 0; i < n; i++) {
own.get(arr[i]).add(i);
}
for (int i = 0; i < k; i++) {
for (int j = 0; j < k; j++) {
if (i != j && map[i][j] == 1) {
nexts.get(i).add(j);
}
}
}
// 放入的数据是一个长度为2的数组
// 0 : 来到的地点号
// 1 : 距离0号地点有多远根据代价排序
// 堆里,小根堆 : {7, 10}
PriorityQueue<int[]> heap = new PriorityQueue<>((a, b) -> a[1] - b[1]);
heap.add(new int[] { 0, 0 });
boolean[] visited = new boolean[n];
while (!heap.isEmpty()) {
int[] cur = heap.poll();
int position = cur[0];
int cost = cur[1];
if (!visited[position]) {
visited[position] = true;
if (position == n - 1) {
return cost;
}
// 你来到了一个地点,拿出型号!
int model = arr[position];
// 5 : {0,3,5,7}
// 0: ....
// 3: ....
// 5: ....
// 7: ....
for (int nextModel : nexts.get(model)) {
for (int nextPosition : own.get(nextModel)) {
if (!visited[nextPosition]) {
heap.add(new int[] { nextPosition, cost + Math.abs(nextPosition - position) });
}
}
}
}
}
return -1;
}
}

@ -0,0 +1,231 @@
package class_2023_05_4_week;
import java.util.HashSet;
// 来自招商银行
// 这个题是个破题,但这可能是经常遇到的一种题,
// 讲一下思路即可,没有必要投入大多时间给这种题目
// 一共有三个服务A、B、C网络延时分别为a、b、c
// 并且一定有1 <= a <= b <= c <= 10^9
// 但是具体的延时数字丢失了,只有单次调用的时间
// 一次调用不可能重复使用相同的服务,
// 一次调用可能使用了三个服务中的某1个、某2个或者全部3个服务
// 比如一个调用的时间T = 100
// 100的延时可能来自以下7种情况
// a = 100这次调用可能单独使用了A
// b = 100这次调用可能单独使用了B
// c = 100这次调用可能单独使用了C
// a + b = 100这次调用可能组合使用了A、B
// a + c = 100这次调用可能组合使用了A、C
// b + c = 100这次调用可能组合使用了B、C
// a + b + c = 100这次调用可能组合使用了A、B、C全部服务
// 那么可想而知如果给的调用时间足够多是可以猜测出a、b、c的
// 给定一个数组times长度为n并且一定有4 <= n <= 7
// times[i] = s表示i号调用用时s而且times中一定都是正数且没有重复值
// 请根据n次调用猜测出a、b、c三元组可能的情况数
// 如果任何a、b、c都无法匹配上给定的调用耗时返回0
// 测试的次数T <= 100
// 也就是说一共最多给定100个数组每一次让你返回a、b、c三元组可能的情况数
public class Code05_NetworkDelayGuess {
// public static void zuo() {
//
// // status[0] : a
// // status[1] : b
// // status[2] : c
// // status[3] : a + b
// // status[4] : a + c
// // status[5] : b + c
// // status[6] : a + b + c
// // status[4] = 0, a + c 没被选中!
// // status[4] > 0 , a + c 250
// int[] status = new int[7];
// int[] values = { 100, 50, 150, 250 };
//
// }
//
// public static void test(int[] status, int[] values, int i) {
// if (i == values.length) {
// // 分配之后a、b、c求出来
// } else {
// // values[i] :
// for (int j = 0; j < status.length; j++) {
// if (status[j] == 0) {
// status[j] = values[i];
// test(status, values, i + 1);
// status[j] = 0;
// }
// }
// }
//
// }
// 这个函数在测试时最多会调用100次100次的整体运行时间1s以内
public static int ways(int[] times) {
int[] status = new int[7];
HashSet<String> ans = new HashSet<>();
process(times, 0, status, ans);
return ans.size();
}
// status[0] : a
// status[1] : b
// status[2] : c
// status[3] : a + b
// status[4] : a + c
// status[5] : b + c
// status[6] : a + b + c
public static void process(int[] times, int i, int[] status, HashSet<String> ans) {
if (i == times.length) {
int a = 0;
int b = 0;
int c = 0;
int cnt = counts(status);
if (cnt == 0) {
a = (status[3] + status[4] - status[5]) / 2;
b = (status[3] + status[5] - status[4]) / 2;
c = (status[4] + status[5] - status[3]) / 2;
} else if (cnt == 1) {
if (status[0] != 0) {
a = status[0];
if (status[3] != 0) {
b = status[3] - a;
}
if (status[4] != 0) {
c = status[4] - a;
}
if (status[5] != 0) {
if (b != 0 && c == 0) {
c = status[5] - b;
}
if (c != 0 && b == 0) {
b = status[5] - c;
}
}
} else if (status[1] != 0) {
b = status[1];
if (status[3] != 0) {
a = status[3] - b;
}
if (status[5] != 0) {
c = status[5] - b;
}
if (status[4] != 0) {
if (a != 0 && c == 0) {
c = status[4] - a;
}
if (c != 0 && a == 0) {
a = status[4] - c;
}
}
} else {
c = status[2];
if (status[4] != 0) {
a = status[4] - c;
}
if (status[5] != 0) {
b = status[5] - c;
}
if (status[3] != 0) {
if (a != 0 && b == 0) {
b = status[3] - a;
}
if (b != 0 && a == 0) {
a = status[3] - b;
}
}
}
} else if (cnt == 2) {
if (status[0] != 0) {
a = status[0];
}
if (status[1] != 0) {
b = status[1];
}
if (status[2] != 0) {
c = status[2];
}
if (a == 0) {
if (status[3] != 0) {
a = status[3] - b;
} else if (status[4] != 0) {
a = status[4] - c;
} else {
a = status[6] - b - c;
}
} else if (b == 0) {
if (status[3] != 0) {
b = status[3] - a;
} else if (status[5] != 0) {
b = status[5] - c;
} else {
b = status[6] - a - c;
}
} else {
if (status[4] != 0) {
c = status[4] - a;
} else if (status[5] != 0) {
c = status[5] - b;
} else {
c = status[6] - a - b;
}
}
} else {
a = status[0];
b = status[1];
c = status[2];
}
if (verify(status, a, b, c)) {
ans.add(a + "_" + b + "_" + c);
}
} else {
for (int j = 0; j < 7; j++) {
if (status[j] == 0) {
status[j] = times[i];
process(times, i + 1, status, ans);
status[j] = 0;
}
}
}
}
public static int counts(int[] status) {
int cnt = 0;
for (int i = 0; i < 3; i++) {
if (status[i] != 0) {
cnt++;
}
}
return cnt;
}
// a <= b <= c
public static boolean verify(int[] s, int a, int b, int c) {
if (a <= 0 || b <= 0 || c <= 0 || a > b || b > c) {
return false;
}
if (s[0] != 0 && s[0] != a) {
return false;
}
if (s[1] != 0 && s[1] != b) {
return false;
}
if (s[2] != 0 && s[2] != c) {
return false;
}
if (s[3] != 0 && s[3] != a + b) {
return false;
}
if (s[4] != 0 && s[4] != a + c) {
return false;
}
if (s[5] != 0 && s[5] != b + c) {
return false;
}
if (s[6] != 0 && s[6] != a + b + c) {
return false;
}
return true;
}
}

@ -0,0 +1,145 @@
package class_2023_05_4_week;
import java.util.ArrayList;
import java.util.Arrays;
// 来自招商银行
// 原始题目描述
// 假如某公司目前推出了N个在售的金融产品(1<=N<=100)
// 对于张三用ai表示他购买了ai(0<=ai<=10^4)份额的第i个产品(1<=i<=N)
// 现给出K(1<=K<=N)个方案,通过这些方案,能够支持将多个不同的产品进行整合
// (也可以对单个产品进行优化)形成新的产品。
// 新的产品形成后,若用户持有了组成新产品所需的全部的原产品份额,
// 则能够将用户持有的原产品份额转换为新产品的份额各原产品份额与新产品份额比例均为1:1
// 我们保证对于每个产品最多存在一个方案使用旧产品整合成该产品
// 并且根据方案产出的新产品的产品编号均大于各旧产品的产品编号
// 现计划根据这些方案,帮助部分愿意升级到最新产品的用户对产品进行升级
// 请协助工作人员计算当前用户能够转换出的最新产品份额的最大值
// 输入描述
// 第一行包含整数N第二行包含N个整数ai第三行包含整数K
// 接下来的K行每一行代表一个方案每一行包含整数1和M(M>=1)
// L为该方案产生的新产品的编号M代表方案所需原产品个数
// 接下来的M个整数代表了该方案所需的每个原产品的个数
// 输出描述
// 根据日前的份额和给出的方案经过若干次转换输出当前用户能够得到产品N的份额最大值
// 举例
// 输入:
// 5
// 2 0 0 1 0
// 3
// 5 2 3 4
// 2 1 1
// 3 1 2
// 输出:
// 1
// 解释:
// 第一步将1份1产品转化为1份2产品
// 第二步将1份2产品转化为1份3产品
// 第三步将1份3产品和1份4产品转成为1份5产品
// 然后不能得到更多的5产品了所以返回1
// 实在是太困惑了,上文说的意思可谓不做人,那么我们改写一下意思,变得好理解
// 如下是改写后的题目描述
// 给定一个数组arr长度为n产品编号从0~n-1
// arr[i]代表初始时i号产品有多少份
// 存在一系列的产品转化方案的数组convert长度为k代表k个方案
// 比如具体某一个方案convert[j] = {a, b, c, d, ...}
// 表示当前的j号方案转化出来的产品是a转化1份a需要1份b、1份c、1份d...
// 其中a、b、c、d...一定都在0~n-1范围内
// 并且题目保证a > Math.max(b, c, d, ....)
// 而且题目保证所有方案转化出来的产品编号一定是不重复的
// 请返回最终能得到的第n-1号商品的最大值
// 1 <= n <= 100
// 0 <= arr[i] <= 10^4
// k < n
public class Code06_ConversionOfFinancialProducts {
public static int MAXN = 101;
public static int[] indegree = new int[MAXN];
public static int[] help = new int[MAXN];
public static int[] zeroQueue = new int[MAXN];
public static int[] need = new int[MAXN];
public static int n;
public static int maxValue(int[] arr, int[][] convert) {
n = arr.length;
ArrayList<ArrayList<Integer>> graph = new ArrayList<>();
for (int i = 0; i < n; i++) {
graph.add(new ArrayList<>());
}
Arrays.fill(indegree, 0, n, 0);
for (int[] relation : convert) {
for (int i = 1; i < relation.length; i++) {
graph.get(relation[0]).add(relation[i]);
indegree[relation[i]]++;
}
}
// arr[n-1] 初始就有100份
// 101 ~ 整体累加和
int l = arr[n - 1] + 1;
int r = 0;
for (int num : arr) {
r += num;
}
int m = 0, ans = arr[n - 1];
while (l <= r) {
m = (l + r) / 2;
if (ok(arr, graph, m)) {
ans = m;
l = m + 1;
} else {
r = m - 1;
}
}
return ans;
}
// arr里是每一种商品的初始份额
// graph就是图3 -> 0 1 2
// aim目标一定要转化出这么多份n-1号商品
public static boolean ok(int[] arr, ArrayList<ArrayList<Integer>> graph, int aim) {
int l = 0;
int r = 0;
for (int i = 0; i < n; i++) {
help[i] = indegree[i];
if (help[i] == 0) {
zeroQueue[r++] = i;
}
}
Arrays.fill(need, 0, n, 0);
need[n - 1] = aim;
while (l < r) {
// 当前商品的编号!
int cur = zeroQueue[l++];
// 需要底层给它供应的量
int supplement = Math.max(need[cur] - arr[cur], 0);
if (graph.get(cur).isEmpty() && supplement > 0) {
return false;
}
for (int next : graph.get(cur)) {
need[next] += supplement;
if (--help[next] == 0) {
zeroQueue[r++] = next;
}
}
}
return true;
}
public static void main(String[] args) {
int[] arr1 = { 2, 0, 0, 1, 0 };
int[][] convert1 = { { 4, 2, 3 }, { 1, 0 }, { 2, 1 } };
System.out.println(maxValue(arr1, convert1));
// 我构造了一个非常好的例子
// 课上说明一下
int[] arr2 = { 100, 5, 5, 0 };
int[][] convert2 = { { 1, 0 }, { 2, 0, 1 }, { 3, 0, 1, 2 } };
System.out.println(maxValue(arr2, convert2));
}
}

@ -3675,6 +3675,120 @@ v是中心点依次打印 : v u o p q w ....
第070节 2023年5月第4周流行算法题目解析
先来一个智力题,来自美团面试题
给定n个二维坐标表示在二维平面的n个点
坐标为double类型精度最多小数点后两位
希望在二维平面上画一个圆圈住其中的k个点其他的n-k个点都要在圆外
返回一个圆心和半径表示哪个圆可以圈住其中的k个点
坐标和半径都是double类型最多保留小数点后两位
下面是正式题目
给你一个整数数组 arr 和一个整数 k
现需要从数组中恰好移除 k 个元素
请找出移除后数组中不同整数的最少数目
测试链接 : https://leetcode.cn/problems/least-number-of-unique-integers-after-k-removals/
来自华为
一个数字n一定要分成k份
得到的乘积尽量大是多少
数字n和k可能非常大到达10^12规模
结果可能更大所以返回结果对1000000007取模
来自美团
给定 pushed 和 popped 两个序列,每个序列中的 值都不重复,
只有当它们可能是在最初空栈上进行的推入 push 和弹出 pop 操作序列的结果时,
返回 true否则返回 false 。
测试链接 : https://leetcode.cn/problems/validate-stack-sequences/
来自招商银行
给定一个数组arr长度为n表示有0~n-1号设备
arr[i]表示i号设备的型号型号的种类从0~k-1一共k种型号
给定一个k*k的矩阵map来表示型号之间的兼容情况
map[a][b] == 1表示a型号兼容b型号
map[a][b] == 0表示a型号不兼容b型号
兼容关系是有向图也就是a型号兼容b型号不代表b型号同时兼容a型号
如果i设备的型号兼容j设备的型号那么可以从i设备修建一条去往j设备的线路
修建线路的代价是i设备到j设备的距离|i-j|
你的目标是从0号设备到达n-1号设备并不一定每个设备都联通只需要到达即可
返回最小的修建代价,如果就是无法到达返回-1
1 <= n <= 1000
1 <= k <= 50
来自招商银行
这个题是个破题,但这可能是经常遇到的一种题,
讲一下思路即可,没有必要投入大多时间给这种题目
一共有三个服务A、B、C网络延时分别为a、b、c
并且一定有1 <= a <= b <= c <= 10^9
但是具体的延时数字丢失了,只有单次调用的时间
一次调用不可能重复使用相同的服务,
一次调用可能使用了三个服务中的某1个、某2个或者全部3个服务
比如一个调用的时间T = 100
100的延时可能来自以下7种情况
a = 100这次调用可能单独使用了A
b = 100这次调用可能单独使用了B
c = 100这次调用可能单独使用了C
a + b = 100这次调用可能组合使用了A、B
a + c = 100这次调用可能组合使用了A、C
b + c = 100这次调用可能组合使用了B、C
a + b + c = 100这次调用可能组合使用了A、B、C全部服务
那么可想而知如果给的调用时间足够多是可以猜测出a、b、c的
给定一个数组times长度为n并且一定有4 <= n <= 7
times[i] = s表示i号调用用时s而且times中一定都是正数且没有重复值
请根据n次调用猜测出a、b、c三元组可能的情况数
如果任何a、b、c都无法匹配上给定的调用耗时返回0
测试的次数T <= 100
也就是说一共最多给定100个数组每一次让你返回a、b、c三元组可能的情况数
来自招商银行
原始题目描述
假如某公司目前推出了N个在售的金融产品(1<=N<=100)
对于张三用ai表示他购买了ai(0<=ai<=10^4)份额的第i个产品(1<=i<=N)
现给出K(1<=K<=N)个方案,通过这些方案,能够支持将多个不同的产品进行整合
(也可以对单个产品进行优化)形成新的产品。
新的产品形成后,若用户持有了组成新产品所需的全部的原产品份额,
则能够将用户持有的原产品份额转换为新产品的份额各原产品份额与新产品份额比例均为1:1
我们保证对于每个产品最多存在一个方案使用旧产品整合成该产品
并且根据方案产出的新产品的产品编号均大于各旧产品的产品编号
现计划根据这些方案,帮助部分愿意升级到最新产品的用户对产品进行升级
请协助工作人员计算当前用户能够转换出的最新产品份额的最大值
输入描述
第一行包含整数N第二行包含N个整数ai第三行包含整数K
接下来的K行每一行代表一个方案每一行包含整数1和M(M>=1)
L为该方案产生的新产品的编号M代表方案所需原产品个数
接下来的M个整数代表了该方案所需的每个原产品的个数
输出描述
根据日前的份额和给出的方案经过若干次转换输出当前用户能够得到产品N的份额最大值
举例
输入:
5
2 0 0 1 0
3
5 2 3 4
2 1 1
3 1 2
输出:
1
解释:
第一步将1份1产品转化为1份2产品
第二步将1份2产品转化为1份3产品
第三步将1份3产品和1份4产品转成为1份5产品
然后不能得到更多的5产品了所以返回1
实在是太困惑了,上文说的意思可谓不做人,那么我们改写一下意思,变得好理解
如下是改写后的题目描述
给定一个数组arr长度为n产品编号从0~n-1
arr[i]代表初始时i号产品有多少份
存在一系列的产品转化方案的数组convert长度为k代表k个方案
比如具体某一个方案convert[j] = {a, b, c, d, ...}
表示当前的j号方案转化出来的产品是a转化1份a需要1份b、1份c、1份d...
其中a、b、c、d...一定都在0~n-1范围内
并且题目保证a > Math.max(b, c, d, ....)
而且题目保证所有方案转化出来的产品编号一定是不重复的
请返回最终能得到的第n-1号商品的最大值
1 <= n <= 100
0 <= arr[i] <= 10^4
k < n

Loading…
Cancel
Save