You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

165 lines
4.2 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

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]);
}
}
}
}
}
}