modify code

master
algorithmzuo 2 years ago
parent 71f1a3bf01
commit af099345f2

@ -0,0 +1,51 @@
package class_2023_03_5_week;
// 来自百度
// 用r、e、d三种字符拼出一个回文子串数量等于x的字符串
// 1 <= x <= 10^5
public class Code01_CharRedToPalindromeString {
public static String palindromeX(int x) {
StringBuilder builder = new StringBuilder();
char cur = 'r';
while (x > 0) {
// 根据x找到当前需要几个相同字符来凑回文子串数量
// x = 12, 1 3 6 10 15
// 1 2 3 4 5
int number = near(x);
for (int i = 0; i < number; i++) {
builder.append(cur);
}
// 4 -> 10
x -= number * (number + 1) / 2;
cur = cur == 'r' ? 'e' : (cur == 'e' ? 'd' : 'r');
}
return builder.toString();
}
public static int near(int x) {
int l = 1;
int r = x;
int m, ans = 0;
while (l <= r) {
m = (l + r) / 2;
if (ok(m, x)) {
ans = m;
l = m + 1;
} else {
r = m - 1;
}
}
return ans;
}
public static boolean ok(int n, int x) {
return ((long) n * (n + 1) / 2) <= x;
}
public static void main(String[] args) {
int x = 13;
System.out.println(palindromeX(x));
}
}

@ -0,0 +1,77 @@
package class_2023_03_5_week;
// 来自腾讯音乐
// 给定一棵树一共有n个点
// 每个点上没有值请把1~n这些数字不重复的分配到二叉树上
// 做到 : 奇数层节点的值总和 与 偶数层节点的值总和 相差不超过1
// 返回奇数层节点分配值的一个方案
// 2 <= n <= 10^5
// 所以课上会讲怎么转化,然后如下的代码实现
public class Code02_OddLevelEvenLevelSumClosed {
// 1 ~ n 奇数节点的个数是k个
// 返回奇数节点的值有哪些
public static int[] team(int n, int k) {
// 1 ~ n , sum = 10 k个奇数 5
// 1 ~ n , sum = 15 k个奇数 7 8
int sum = (n + 1) * n / 2;
int p1 = sum / 2;
int p2 = (sum + 1) / 2;
int[] ans = generate(p1, n, k);
if (ans == null && (sum & 1) == 1) {
ans = generate(p2, n, k);
}
return ans != null ? ans : new int[] { -1 };
}
// 一共 1 ~ n 这些数字
// 其中选k个数字
// 一定要让k个数字的累加和是wantSum
// 返回哪k个数字只要返回一种方法就可以
public static int[] generate(int wantSum, int n, int k) {
// k个数字和最小的情况1 2 3 ... k
int sumMinK = (k + 1) * k / 2;
// 每个数提升的幅度
int range = n - k;
if (wantSum < sumMinK || wantSum > sumMinK + k * range) {
return null;
}
int add = wantSum - sumMinK;
int rightSize = add / range;
int midIndex = (k - rightSize) + (add % range);
int leftSize = k - rightSize - (add % range == 0 ? 0 : 1);
int[] ans = new int[k];
for (int i = 0; i < leftSize; i++) {
ans[i] = i + 1;
}
if (add % range != 0) {
ans[leftSize] = midIndex;
}
for (int i = k - 1, j = 0; j < rightSize; i--, j++) {
ans[i] = n - j;
}
return ans;
}
public static void main(String[] args) {
// n是最大值1~n这些数字都有
int n = 100;
// k是个数
int k = 33;
// 1~n这些数字选k个能不能求和逼近一半
// 返回方案
int[] ans = team(n, k);
System.out.println("总和 : " + (n + 1) * n / 2);
System.out.println("长度 : " + ans.length);
int sum = 0;
System.out.print("数字 : ");
for (int num : ans) {
System.out.print(num + " ");
sum += num;
}
System.out.println();
System.out.println("求和 : " + sum);
System.out.println("剩余 : " + ((n + 1) * n / 2 - sum));
}
}

@ -0,0 +1,92 @@
package class_2023_03_5_week;
import java.util.Arrays;
// 来自小红书、字节跳动
// 村里面一共有 n 栋房子
// 我们希望通过建造水井和铺设管道来为所有房子供水。
// 对于每个房子 i我们有两种可选的供水方案
// 一种是直接在房子内建造水井
// 成本为 wells[i - 1] (注意 -1 ,因为 索引从0开始
// 另一种是从另一口井铺设管道引水
// 数组 pipes 给出了在房子间铺设管道的成本
// 其中每个 pipes[j] = [house1j, house2j, costj]
// 代表用管道将 house1j 和 house2j连接在一起的成本。连接是双向的。
// 请返回 为所有房子都供水的最低总成本 。
// 这道题很高频,引起注意
// 本身也不难,转化一下变成最小生成树的问题即可
// 测试链接 : https://leetcode.cn/problems/optimize-water-distribution-in-a-village/
public class Code03_OptimizeWaterDistributionInVillage {
public static int MAXN = 10010;
public static int[][] edges = new int[MAXN << 1][3];
public static int esize;
public static int[] father = new int[MAXN];
public static int[] size = new int[MAXN];
public static int[] help = new int[MAXN];
public static void build(int n) {
for (int i = 0; i <= n; i++) {
father[i] = i;
size[i] = 1;
}
}
public static int find(int i) {
int s = 0;
while (i != father[i]) {
help[s++] = i;
i = father[i];
}
while (s > 0) {
father[help[--s]] = i;
}
return i;
}
public static boolean union(int i, int j) {
int f1 = find(i);
int f2 = find(j);
if (f1 != f2) {
if (size[f1] >= size[f2]) {
father[f2] = f1;
size[f1] += size[f2];
} else {
father[f1] = f2;
size[f2] += size[f1];
}
return true;
} else {
return false;
}
}
public static int minCostToSupplyWater(int n, int[] wells, int[][] pipes) {
esize = 0;
for (int i = 0; i < n; i++, esize++) {
edges[esize][0] = 0;
edges[esize][1] = i + 1;
edges[esize][2] = wells[i];
}
for (int i = 0; i < pipes.length; i++, esize++) {
edges[esize][0] = pipes[i][0];
edges[esize][1] = pipes[i][1];
edges[esize][2] = pipes[i][2];
}
Arrays.sort(edges, 0, esize, (a, b) -> a[2] - b[2]);
build(n);
int ans = 0;
for (int i = 0; i < esize; i++) {
if (union(edges[i][0], edges[i][1])) {
ans += edges[i][2];
}
}
return ans;
}
}

@ -0,0 +1,66 @@
package class_2023_03_5_week;
// 来自学员问题,蓝桥杯练习题
// 小青蛙住在一条河边, 它想到河对岸的学校去学习
// 小青蛙打算经过河里 的石头跳到对岸
// 河里的石头排成了一条直线, 小青蛙每次跳跃必须落在一块石头或者岸上
// 给定一个长度为n的数组arr表示每块儿石头的高度数值
// 每块石头有一个高度, 每次小青蛙从一块石头起跳
// 这块石头的高度就会下降1, 当石头的高度下降到0时
// 小青蛙不能再跳到这块石头上(跳跃后使石头高度下降到0是允许的)
// 小青蛙一共需要去学校上x天课, 所以它需要往返x次(去x次回x次)
// 当小青蛙具有 一个跳跃能力y时, 它能跳不超过y的距离
// 请问小青蛙的跳跃能力至少是多少才能用这些石头上完x次课
// 1 <= n <= 10^5
// 1 <= arr[i] <= 10^4
// 1 <= x <= 10^9
// 测试链接 : https://www.luogu.com.cn/problem/P8775
// 请同学们务必参考如下代码中关于输入、输出的处理
// 这是输入输出处理效率很高的写法
// 提交以下的所有代码,并把主类名改成"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;
public class Code04_FrogGoToSchool {
public static int MAXN = 100001;
public static long[] help = new long[MAXN];
public static int n, x;
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) {
n = (int) in.nval;
in.nextToken();
x = (int) (in.nval);
for (int i = 1; i < n; i++) {
in.nextToken();
help[i] = help[i - 1] + (int) in.nval;
}
out.println(minAbility());
out.flush();
}
}
// O(N)的最优解
public static int minAbility() {
int ans = 0;
for (int l = 1, r = 1; l < n; l++) {
while (r < n && help[r] - help[l - 1] < 2L * x) {
r++;
}
ans = Math.max(ans, r - l + 1);
}
return ans;
}
}

@ -0,0 +1,135 @@
package class_2023_03_5_week;
import java.util.ArrayList;
import java.util.Arrays;
// 来自谷歌
// 给你一棵 n 个节点的树(连通无向无环的图)
// 节点编号从 0 到 n - 1 且恰好有 n - 1 条边
// 给你一个长度为 n 下标从 0 开始的整数数组 vals
// 分别表示每个节点的值
// 同时给你一个二维整数数组 edges
// 其中 edges[i] = [ai, bi] 表示节点 ai 和 bi 之间有一条 无向 边
// 一条 好路径 需要满足以下条件:
// 开始节点和结束节点的值 相同 。
// 开始节点和结束节点中间的所有节点值都 小于等于 开始节点的值
//(也就是说开始节点的值应该是路径上所有节点的最大值)
// 请你返回不同好路径的数目
// 注意,一条路径和它反向的路径算作 同一 路径
// 比方说, 0 -> 1 与 1 -> 0 视为同一条路径。单个节点也视为一条合法路径
// 测试链接 : https://leetcode.cn/problems/number-of-good-paths/
public class Code05_NumberOfGoodPaths {
// [1,2]
// 1点 -> 7
// 2点 -> 3
public static int numberOfGoodPaths(int[] vals, int[][] edges) {
int n = vals.length;
// 1) 当前这一种,最经典的建图方式
// 2) 邻接矩阵, N * N, 非常废空间用于点的数量N不大的时候
// 3) 链式前向星,固定数组就可以建图,省空间,不用动态结构,实现麻烦!
ArrayList<ArrayList<Integer>> graph = new ArrayList<>();
for (int i = 0; i < n; i++) {
graph.add(new ArrayList<>());
}
// a -> b b -> a
// a {b}
// b {a}
for (int[] e : edges) {
graph.get(e[0]).add(e[1]);
graph.get(e[1]).add(e[0]);
}
// 所有节点
int[][] nodes = new int[n][2];
for (int i = 0; i < n; i++) {
// 编号4号点
nodes[i][0] = i;
// 值4号点的值
nodes[i][1] = vals[i];
}
Arrays.sort(nodes, (a, b) -> a[1] - b[1]);
UnionFind uf = new UnionFind(n);
// 标签只有maxIndex数组
int[] maxIndex = new int[n];
for (int i = 0; i < n; i++) {
maxIndex[i] = i;
}
// maxCnts不是标签
// 单纯的最大值次数统计
int[] maxCnts = new int[n];
Arrays.fill(maxCnts, 1);
int ans = n;
// 已经根据值排序了!一定是从值小的点,遍历到值大的点
for (int[] node : nodes) {
int curi = node[0];
int curv = vals[curi];
int curCandidate = uf.find(curi);
int curMaxIndex = maxIndex[curCandidate];
// 遍历邻居
for (int nexti : graph.get(curi)) {
// 邻居值
int nextv = vals[nexti];
// 邻居的集合代表点
int nextCandidate = uf.find(nexti);
if (curCandidate != nextCandidate && curv >= nextv) {
// 邻居集合最大值的下标
int nextMaxIndex = maxIndex[nextCandidate];
if (curv == vals[nextMaxIndex]) {
ans += maxCnts[curMaxIndex] * maxCnts[nextMaxIndex];
maxCnts[curMaxIndex] += maxCnts[nextMaxIndex];
}
int candidate = uf.union(curi, nexti);
maxIndex[candidate] = curMaxIndex;
}
}
}
return ans;
}
public static class UnionFind {
private int[] parent;
private int[] size;
private int[] help;
public UnionFind(int n) {
parent = new int[n];
size = new int[n];
help = new int[n];
for (int i = 0; i < n; i++) {
parent[i] = i;
size[i] = 1;
}
}
public 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 int 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;
return f1;
} else {
size[f2] += size[f1];
parent[f1] = f2;
return f2;
}
}
return f1;
}
}
}

@ -3309,13 +3309,65 @@ projects[i] = {a, b}
第064节 2023年3月第5周流行算法题目解析
来自百度
用r、e、d三种字符拼出一个回文子串数量等于x的字符串
1 <= x <= 10^5
来自腾讯音乐
给定一棵树一共有n个点
每个点上没有值请把1~n这些数字不重复的分配到二叉树上
做到 : 奇数层节点的值总和 与 偶数层节点的值总和 相差不超过1
返回奇数层节点分配值的一个方案
2 <= n <= 10^5
来自小红书、字节跳动
村里面一共有 n 栋房子
我们希望通过建造水井和铺设管道来为所有房子供水。
对于每个房子 i我们有两种可选的供水方案
一种是直接在房子内建造水井
成本为 wells[i - 1] (注意 -1 ,因为 索引从0开始
另一种是从另一口井铺设管道引水
数组 pipes 给出了在房子间铺设管道的成本
其中每个 pipes[j] = [house1j, house2j, costj]
代表用管道将 house1j 和 house2j连接在一起的成本。连接是双向的。
请返回 为所有房子都供水的最低总成本 。
这道题很高频,引起注意
本身也不难,转化一下变成最小生成树的问题即可
测试链接 : https://leetcode.cn/problems/optimize-water-distribution-in-a-village/
来自学员问题,蓝桥杯练习题
小青蛙住在一条河边, 它想到河对岸的学校去学习
小青蛙打算经过河里 的石头跳到对岸
河里的石头排成了一条直线, 小青蛙每次跳跃必须落在一块石头或者岸上
给定一个长度为n的数组arr表示每块儿石头的高度数值
每块石头有一个高度, 每次小青蛙从一块石头起跳
这块石头的高度就会下降1, 当石头的高度下降到0时
小青蛙不能再跳到这块石头上(跳跃后使石头高度下降到0是允许的)
小青蛙一共需要去学校上x天课, 所以它需要往返x次(去x次回x次)
当小青蛙具有 一个跳跃能力y时, 它能跳不超过y的距离
请问小青蛙的跳跃能力至少是多少才能用这些石头上完x次课
1 <= n <= 10^5
1 <= arr[i] <= 10^4
1 <= x <= 10^9
测试链接 : https://www.luogu.com.cn/problem/P8775
来自谷歌
给你一棵 n 个节点的树(连通无向无环的图)
节点编号从 0 到 n - 1 且恰好有 n - 1 条边
给你一个长度为 n 下标从 0 开始的整数数组 vals
分别表示每个节点的值
同时给你一个二维整数数组 edges
其中 edges[i] = [ai, bi] 表示节点 ai 和 bi 之间有一条 无向 边
一条 好路径 需要满足以下条件:
开始节点和结束节点的值 相同 。
开始节点和结束节点中间的所有节点值都 小于等于 开始节点的值
也就是说开始节点的值应该是路径上所有节点的最大值
请你返回不同好路径的数目
注意,一条路径和它反向的路径算作 同一 路径
比方说, 0 -> 1 与 1 -> 0 视为同一条路径。单个节点也视为一条合法路径
测试链接 : https://leetcode.cn/problems/number-of-good-paths/

Loading…
Cancel
Save