modify code

master
algorithmzuo 2 years ago
parent 3c9a1bca9e
commit df62aeb1fd

@ -0,0 +1,64 @@
package class_2022_10_1_week;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
// 力扣数据中心有 n 台服务器分别按从 0  n-1 的方式进行了编号
// 它们之间以「服务器到服务器」点对点的形式相互连接组成了一个内部集群
// 其中连接 connections 是无向的
// 从形式上讲connections[i] = [a, b] 表示服务器 a 和 b 之间形成连接
// 任何服务器都可以直接或者间接地通过网络到达任何其他服务器。
// "关键连接"是在该集群中的重要连接,也就是说,假如我们将它移除
// 便会导致某些服务器无法访问其他服务器。
// 请你以任意顺序返回该集群内的所有"关键连接"
// 测试链接 : https://leetcode.cn/problems/critical-connections-in-a-network/
public class Code01_CriticalConnectionsInANetwork {
public static int[] dfn = new int[100010];
public static int[] low = new int[100010];
public static int dfnCnt = 0;
public static List<List<Integer>> criticalConnections(int n, List<List<Integer>> connections) {
ArrayList<ArrayList<Integer>> graph = new ArrayList<>();
for (int i = 0; i < n; i++) {
graph.add(new ArrayList<>());
}
for (List<Integer> edge : connections) {
graph.get(edge.get(0)).add(edge.get(1));
graph.get(edge.get(1)).add(edge.get(0));
}
Arrays.fill(dfn, 0, n, 0);
Arrays.fill(low, 0, n, 0);
dfnCnt = 0;
List<List<Integer>> ans = new ArrayList<>();
tarjan(0, -1, graph, ans);
return ans;
}
// tarjan dfs过程
// 点的编号是curdfn X low X
public static void tarjan(int cur, int father,
ArrayList<ArrayList<Integer>> graph,
List<List<Integer>> ans) {
// 第一次来到cur
// 分配dfn、low序号
dfn[cur] = low[cur] = ++dfnCnt;
for (Integer next : graph.get(cur)) {
if (next != father) {
if (dfn[next] == 0) { // 下级的节点没跑过,就去跑
tarjan(next, cur, graph, ans);
low[cur] = Math.min(low[cur], low[next]);
} else { // 下级的节点跑过了直接更新low
low[cur] = Math.min(low[cur], dfn[next]);
}
}
}
if (low[cur] == dfn[cur] && cur != 0) {
ans.add(Arrays.asList(father, cur));
}
}
}

@ -0,0 +1,163 @@
package class_2022_10_1_week;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
// 来自Leetcode周赛
// 魔物了占领若干据点,这些据点被若干条道路相连接,
// roads[i] = [x, y] 表示编号 x、y 的两个据点通过一条道路连接。
// 现在勇者要将按照以下原则将这些据点逐一夺回:
// 在开始的时候,勇者可以花费资源先夺回一些据点,
// 初始夺回第 j 个据点所需消耗的资源数量为 cost[j]
// 接下来,勇者在不消耗资源情况下,
// 每次可以夺回一个和「已夺回据点」相连接的魔物据点,
// 并对其进行夺回
// 为了防止魔物暴动,勇者在每一次夺回据点后(包括花费资源夺回据点后),
// 需要保证剩余的所有魔物据点之间是相连通的(不经过「已夺回据点」)。
// 请返回勇者夺回所有据点需要消耗的最少资源数量。
// 输入保证初始所有据点都是连通的,且不存在重边和自环
// 测试链接 : https://leetcode.cn/problems/s5kipK/
public class Code02_CaptureStrongHold {
public static long minimumCost(int[] cost, int[][] roads) {
int n = cost.length;
if (n == 1) {
return cost[0];
}
int m = roads.length;
DoubleConnectedComponents dc
= new DoubleConnectedComponents(n, m, roads);
long ans = 0;
// dcc {a,b,c} {c,d,e}
if (dc.dcc.size() == 1) {
ans = Integer.MAX_VALUE;
for (int num : cost) {
ans = Math.min(ans, num);
}
} else { // 不只一个点双连通分量
ArrayList<Integer> arr = new ArrayList<>();
for (List<Integer> set : dc.dcc) {
int cutCnt = 0;
int curCost = Integer.MAX_VALUE;
for (int nodes : set) {
if (dc.cut[nodes]) {
cutCnt++;
} else {
curCost = Math.min(curCost, cost[nodes]);
}
}
if (cutCnt == 1) {
arr.add(curCost);
}
}
arr.sort((a, b) -> a - b);
for (int i = 0; i < arr.size() - 1; i++) {
ans += arr.get(i);
}
}
return ans;
}
public static class DoubleConnectedComponents {
public int[] head;
public int[] next;
public int[] to;
public int[] dfn;
public int[] low;
public int[] stack;
public List<List<Integer>> dcc;
public boolean[] cut;
public static int edgeCnt;
public static int dfnCnt;
public static int top;
public static int root;
public DoubleConnectedComponents(int n, int m, int[][] roads) {
init(n, m);
createGraph(roads);
creatDcc(n);
}
private void init(int n, int m) {
head = new int[n];
Arrays.fill(head, -1);
next = new int[m << 1];
to = new int[m << 1];
dfn = new int[n];
low = new int[n];
stack = new int[n];
dcc = new ArrayList<>();
cut = new boolean[n];
edgeCnt = 0;
dfnCnt = 0;
top = 0;
root = 0;
}
private void createGraph(int[][] roads) {
for (int[] edges : roads) {
add(edges[0], edges[1]);
add(edges[1], edges[0]);
}
}
private void add(int u, int v) {
to[edgeCnt] = v;
next[edgeCnt] = head[u];
head[u] = edgeCnt++;
}
private void creatDcc(int n) {
for (int i = 0; i < n; i++) {
// 0 1 2 3 n-1
if (dfn[i] == 0) {
root = i;
tarjan(i);
}
}
}
private void tarjan(int x) {
dfn[x] = low[x] = ++dfnCnt;
stack[top++] = x;
int flag = 0;
if (x == root && head[x] == -1) {
dcc.add(new ArrayList<>());
dcc.get(dcc.size() - 1).add(x);
} else {
// 当前来到的节点是x
// x {a,b,c}
for (int i = head[x]; i >= 0; i = next[i]) {
// y是下级节点
int y = to[i];
if (dfn[y] == 0) { // y点没遍历过
tarjan(y);
if (low[y] >= dfn[x]) { // 正在扎口袋
flag++;
if (x != root || flag > 1) {
cut[x] = true;
}
List<Integer> curAns = new ArrayList<>();
// 从栈里一次弹出节点
// 弹到y停
// 弹出的节点都加入集合x也加入x不弹出
for (int z = stack[--top]; z != y; z = stack[--top]) {
curAns.add(z);
}
curAns.add(y);
curAns.add(x);
dcc.add(curAns);
}
low[x] = Math.min(low[x], low[y]);
} else { // y点已经遍历过了
low[x] = Math.min(low[x], dfn[y]);
}
}
}
}
}
}

@ -0,0 +1,109 @@
package class_2022_10_1_week;
import java.util.Arrays;
// 来自学员问题
// 商场中有一展柜A其大小固定现已被不同的商品摆满
// 商家提供了一些新商品B需要对A中的部分商品进行更新替换
// B中的商品可以自由使用也就是可以用B中的任何商品替换A中的任何商品
// A中的商品一旦被替换就认为消失了而不是回到了B中
// 要求更新过后的展柜中,商品严格按照价格由低到高进行排列
// 不能有相邻商品价格相等的情况
// A[i]为展柜中第i个位置商品的价格B[i]为各个新商品的价格
// 求能够满足A中商品价格严格递增的最小操作次数若无法满足则返回-1
public class Code03_MakeASortedMinSwaps {
// 可以用B里的数字替换A里的数字想让A严格递增
// 返回至少换几个数字
public static int minSwaps(int[] A, int[] B) {
// 根据题意B里的数字随意拿
// 所以B里的数字排序不会影响拿
// 同时A如果从左往右考虑依次被B替换上去的数字肯定是从小到大的
// 这是一定的比如B = {5,3,2,9}
// 可能先用5替换A的某个左边的数再用2替换A的某个右边的数吗不可能
// 所以将B排序
Arrays.sort(B);
int ans = process(A, B, 0, 0, 0);
return ans == Integer.MAX_VALUE ? -1 : ans;
}
// 参数解释:
// A[0...ai-1]范围上已经做到升序了
// 接下来请让A[ai....]范围上的数字做到升序
// 之前的过程中B里可能已经拿过一些数字了
// 拿过的数字都在B[0...bi-1]范围上,不一定都拿了
// 但是最后拿的数字一定是B[bi-1]
// 如果想用B里的数字替换当前的A[ai]请在B[bi....]范围上考虑拿数字
// pre : 表示之前的A[ai-1]被替换了没有,
// 如果pre==0表示A[ai-1]没被替换
// 如果pre==1表示A[ai-1]被替换了被谁替换的被B[bi-1]替换的!
// 返回值让A[ai....]范围上的数字做到升序最少还要在B[bi...]上拿几个数字
// 如果返回值是Integer.MAX_VALUE表示怎么都做不到
// 这就是一个三维动态规划,自己改!
// ai 是N范围
// bi 是M范围
// pre 只有0、1两种值
// 所以时间复杂度O(N*M*logM)
// 这个logM怎么来的二分来的看代码
public static int process(int[] A, int[] B, int ai, int bi, int pre) {
if (ai == A.length) {
return 0;
}
// 之前的数是什么
int preNum = 0;
if (ai == 0) {
preNum = Integer.MIN_VALUE;
} else {
if (pre == 0) {
preNum = A[ai - 1];
} else {
preNum = B[bi - 1];
}
}
// 可能性1搞定当前的A[ai],不依靠交换
int p1 = preNum < A[ai] ? process(A, B, ai + 1, bi, 0) : Integer.MAX_VALUE;
// 可能性2搞定当前的A[ai],依靠交换
int p2 = Integer.MAX_VALUE;
// 在B[bi....]这个范围上,找到>preNum最左的位置
// 这一步是可以二分的因为B整体有序
int nearMoreIndex = bs(B, bi, preNum);
if (nearMoreIndex != -1) {
int next2 = process(A, B, ai + 1, nearMoreIndex + 1, 1);
if (next2 != Integer.MAX_VALUE) {
p2 = 1 + next2;
}
}
return Math.min(p1, p2);
}
// 在B[start....]这个范围上,找到>num最左的位置
public static int bs(int[] B, int start, int num) {
int ans = -1;
int l = start;
int r = B.length - 1;
int m = 0;
while (l <= r) {
m = (l + r) / 2;
if (B[m] > num) {
ans = m;
r = m - 1;
} else {
l = m + 1;
}
}
return ans;
}
public static void main(String[] args) {
int[] A1 = { 1, 8, 3, 6, 9 };
int[] B1 = { 1, 3, 2, 4 };
System.out.println(minSwaps(A1, B1));
int[] A2 = { 4, 8, 3, 10, 5 };
int[] B2 = { 1, 3, 2, 4 };
System.out.println(minSwaps(A2, B2));
int[] A3 = { 1, 8, 3, 6, 9 };
int[] B3 = { 4, 3, 1 };
System.out.println(minSwaps(A3, B3));
}
}

@ -0,0 +1,111 @@
package class_2022_10_1_week;
import java.util.HashMap;
// 来自美团
// 两种颜色的球蓝色和红色都按1n编号共计2n个
// 为方便放在一个数组中,红球编号取负,篮球不变,并打乱顺序,
// 要求同一种颜色的球按编号升序排列,可以进行如下操作:
// 交换相邻两个球,求最少操作次数。
// [3,-3,1,-4,2,-2,-1,4]
// 最终交换结果为
// [1,2,3,-1,-2,-3,-4,4]
// 最少交换次数为10
// n <= 1000
public class Code04_TwoTeamsSortedMinSwap {
// [3,-3,1,-4,2,-2,-1,4]
// -3 -4 -2 -1 -> -1 -2 -3 -4
// 3 1 2 4 -> 1 2 3 4
// 这个题写对数器太麻烦了
// 所以这就是正式解
public static int minSwaps(int[] arr) {
int n = arr.length;
HashMap<Integer, Integer> map = new HashMap<>();
int topA = 0;
int topB = 0;
for (int i = 0; i < n; i++) {
if (arr[i] > 0) {
topA = Math.max(topA, arr[i]);
} else {
topB = Math.max(topB, Math.abs(arr[i]));
}
map.put(arr[i], i);
}
IndexTree it = new IndexTree(n);
for (int i = 0; i < n; i++) {
it.add(i, 1);
}
return f(topA, topB, it, n - 1, map);
}
// 可以改二维动态规划!
// 因为it的状态只由topA和topB决定
// 所以it的状态不用作为可变参数
public static int f(int topA, int topB,
IndexTree it, int end,
HashMap<Integer, Integer> map) {
if (topA == 0 && topB == 0) {
return 0;
}
int p1 = Integer.MAX_VALUE;
int p2 = Integer.MAX_VALUE;
int index, cost, next;
if (topA != 0) {
index = map.get(topA);
cost = it.sum(index, end) - 1;
it.add(index, -1);
next = f(topA - 1, topB, it, end, map);
it.add(index, 1);
p1 = cost + next;
}
if (topB != 0) {
index = map.get(-topB);
cost = it.sum(index, end) - 1;
it.add(index, -1);
next = f(topA, topB - 1, it, end, map);
it.add(index, 1);
p2 = cost + next;
}
return Math.min(p1, p2);
}
public static class IndexTree {
private int[] tree;
private int N;
public IndexTree(int size) {
N = size;
tree = new int[N + 1];
}
public void add(int i, int v) {
i++;
while (i <= N) {
tree[i] += v;
i += i & -i;
}
}
public int sum(int l, int r) {
return l == 0 ? sum(r + 1) : (sum(r + 1) - sum(l));
}
private int sum(int index) {
int ans = 0;
while (index > 0) {
ans += tree[index];
index -= index & -index;
}
return ans;
}
}
public static void main(String[] args) {
int[] arr = { 3, -3, 1, -4, 2, -2, -1, 4 };
System.out.println(minSwaps(arr));
}
}

@ -2064,7 +2064,53 @@ B[i] = { e, f }表示第i条查询想知道e号、f号实验一共有多
第043节 2022年10月第1周流行算法题目解析
力扣数据中心有 n 台服务器分别按从 0  n-1 的方式进行了编号
它们之间以「服务器到服务器」点对点的形式相互连接组成了一个内部集群
其中连接 connections 是无向的
从形式上讲connections[i] = [a, b] 表示服务器 a 和 b 之间形成连接
任何服务器都可以直接或者间接地通过网络到达任何其他服务器。
"关键连接"是在该集群中的重要连接,也就是说,假如我们将它移除
便会导致某些服务器无法访问其他服务器。
请你以任意顺序返回该集群内的所有"关键连接"
测试链接 : https://leetcode.cn/problems/critical-connections-in-a-network/
来自Leetcode周赛
魔物了占领若干据点,这些据点被若干条道路相连接,
roads[i] = [x, y] 表示编号 x、y 的两个据点通过一条道路连接。
现在勇者要将按照以下原则将这些据点逐一夺回:
在开始的时候,勇者可以花费资源先夺回一些据点,
初始夺回第 j 个据点所需消耗的资源数量为 cost[j]
接下来,勇者在不消耗资源情况下,
每次可以夺回一个和「已夺回据点」相连接的魔物据点,
并对其进行夺回
为了防止魔物暴动,勇者在每一次夺回据点后(包括花费资源夺回据点后),
需要保证剩余的所有魔物据点之间是相连通的(不经过「已夺回据点」)。
请返回勇者夺回所有据点需要消耗的最少资源数量。
输入保证初始所有据点都是连通的,且不存在重边和自环
测试链接 : https://leetcode.cn/problems/s5kipK/
来自学员问题
商场中有一展柜A其大小固定现已被不同的商品摆满
商家提供了一些新商品B需要对A中的部分商品进行更新替换
B中的商品可以自由使用也就是可以用B中的任何商品替换A中的任何商品
A中的商品一旦被替换就认为消失了而不是回到了B中
要求更新过后的展柜中,商品严格按照价格由低到高进行排列
不能有相邻商品价格相等的情况
A[i]为展柜中第i个位置商品的价格B[i]为各个新商品的价格
求能够满足A中商品价格严格递增的最小操作次数若无法满足则返回-1
来自美团
两种颜色的球蓝色和红色都按1n编号共计2n个
为方便放在一个数组中,红球编号取负,篮球不变,并打乱顺序,
要求同一种颜色的球按编号升序排列,可以进行如下操作:
交换相邻两个球,求最少操作次数。
[3,-3,1,-4,2,-2,-1,4]
最终交换结果为
[1,2,3,-1,-2,-3,-4,4]
最少交换次数为10
n <= 1000

Loading…
Cancel
Save