Skip to content

Commit b527396

Browse files
committed
DorogayaSet
1 parent 0f038b3 commit b527396

1 file changed

Lines changed: 293 additions & 0 deletions

File tree

Lines changed: 293 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,293 @@
1+
/*
2+
* Принцип работы алгоритма:
3+
* Нужно найти максимальное остовное дерево, поэтому используем алгоритм Краскала
4+
* в обратном порядке: сортируем все рёбра по убыванию веса и последовательно
5+
* добавляем ребро в ответ, если оно соединяет две разные компоненты связности.
6+
* Для проверки компонент используем DSU.
7+
*
8+
* Петли не влияют на ответ: ребро из вершины в саму себя не соединяет разные
9+
* компоненты, поэтому DSU его не добавит. Кратные рёбра обрабатываются естественно:
10+
* из них раньше будут рассмотрены более тяжёлые.
11+
*
12+
* Почему алгоритм корректен:
13+
* В алгоритме Краскала на каждом шаге выбирается самое тяжёлое ребро, которое
14+
* не создаёт цикл. По свойству разреза для максимального остовного дерева такое
15+
* ребро можно безопасно добавить в некоторый оптимальный остов. Повторяя этот
16+
* шаг, получаем максимальное остовное дерево. Если после обработки всех рёбер
17+
* выбрано меньше n - 1 ребра, значит граф несвязный и остовного дерева нет.
18+
*
19+
* Временная сложность: O(m log m) из-за сортировки рёбер.
20+
* Операции DSU работают почти за O(1), точнее O(alpha(n)).
21+
* Пространственная сложность: O(n + m), где n — число вершин, m — число рёбер.
22+
*/
23+
24+
import java.io.EOFException;
25+
import java.io.IOException;
26+
import java.io.InputStream;
27+
import java.io.OutputStream;
28+
import java.util.Arrays;
29+
30+
public class DorogayaSet {
31+
32+
static final String FAIL = "Oops! I did it again";
33+
34+
static long solve(int n, Edge[] edges) {
35+
Arrays.sort(edges, (a, b) -> Integer.compare(b.weight, a.weight));
36+
37+
DSU dsu = new DSU(n);
38+
long totalWeight = 0;
39+
int usedEdges = 0;
40+
41+
for (Edge edge : edges) {
42+
if (dsu.union(edge.from, edge.to)) {
43+
totalWeight += edge.weight;
44+
usedEdges++;
45+
46+
if (usedEdges == n - 1) {
47+
break;
48+
}
49+
}
50+
}
51+
52+
if (usedEdges != n - 1) {
53+
return -1;
54+
}
55+
56+
return totalWeight;
57+
}
58+
59+
static final class Edge {
60+
final int from;
61+
final int to;
62+
final int weight;
63+
64+
Edge(int from, int to, int weight) {
65+
this.from = from;
66+
this.to = to;
67+
this.weight = weight;
68+
}
69+
}
70+
71+
static final class DSU {
72+
private final int[] parent;
73+
private final int[] size;
74+
75+
DSU(int n) {
76+
parent = new int[n + 1];
77+
size = new int[n + 1];
78+
79+
for (int i = 1; i <= n; i++) {
80+
parent[i] = i;
81+
size[i] = 1;
82+
}
83+
}
84+
85+
int find(int v) {
86+
if (parent[v] != v) {
87+
parent[v] = find(parent[v]);
88+
}
89+
return parent[v];
90+
}
91+
92+
boolean union(int a, int b) {
93+
int rootA = find(a);
94+
int rootB = find(b);
95+
96+
if (rootA == rootB) {
97+
return false;
98+
}
99+
100+
if (size[rootA] < size[rootB]) {
101+
int tmp = rootA;
102+
rootA = rootB;
103+
rootB = tmp;
104+
}
105+
106+
parent[rootB] = rootA;
107+
size[rootA] += size[rootB];
108+
return true;
109+
}
110+
}
111+
112+
// -------------------- FAST INPUT --------------------
113+
static final class FastIn {
114+
private final InputStream in;
115+
private final byte[] buf = new byte[1 << 16];
116+
private int ptr = 0;
117+
private int len = 0;
118+
119+
FastIn(InputStream in) {
120+
this.in = in;
121+
}
122+
123+
private int read() throws IOException {
124+
if (ptr >= len) {
125+
len = in.read(buf);
126+
ptr = 0;
127+
128+
if (len <= 0) {
129+
return -1;
130+
}
131+
}
132+
133+
return buf[ptr++];
134+
}
135+
136+
int nextInt() throws IOException {
137+
int c;
138+
139+
do {
140+
c = read();
141+
142+
if (c == -1) {
143+
throw new EOFException("Unexpected EOF");
144+
}
145+
} while (c <= ' ');
146+
147+
int sign = 1;
148+
149+
if (c == '-') {
150+
sign = -1;
151+
c = read();
152+
}
153+
154+
int val = 0;
155+
156+
while (c > ' ') {
157+
val = val * 10 + (c - '0');
158+
c = read();
159+
}
160+
161+
return val * sign;
162+
}
163+
}
164+
165+
// -------------------- FAST OUTPUT --------------------
166+
static final class FastOut {
167+
private final OutputStream out;
168+
private final byte[] buf = new byte[1 << 16];
169+
private int p = 0;
170+
private final byte[] tmp = new byte[20];
171+
172+
FastOut(OutputStream out) {
173+
this.out = out;
174+
}
175+
176+
void writeByte(int b) throws IOException {
177+
if (p == buf.length) {
178+
flush();
179+
}
180+
181+
buf[p++] = (byte) b;
182+
}
183+
184+
void writeString(String s) throws IOException {
185+
for (int i = 0; i < s.length(); i++) {
186+
writeByte(s.charAt(i));
187+
}
188+
}
189+
190+
void writeLong(long x) throws IOException {
191+
if (x == 0) {
192+
writeByte('0');
193+
return;
194+
}
195+
196+
if (x < 0) {
197+
writeByte('-');
198+
x = -x;
199+
}
200+
201+
int k = 0;
202+
203+
while (x > 0) {
204+
tmp[k++] = (byte) ('0' + (x % 10));
205+
x /= 10;
206+
}
207+
208+
for (int i = k - 1; i >= 0; i--) {
209+
writeByte(tmp[i]);
210+
}
211+
}
212+
213+
void flush() throws IOException {
214+
out.write(buf, 0, p);
215+
p = 0;
216+
}
217+
}
218+
219+
private static void run() throws Exception {
220+
FastIn in = new FastIn(System.in);
221+
FastOut out = new FastOut(System.out);
222+
223+
int n = in.nextInt();
224+
int m = in.nextInt();
225+
226+
Edge[] edges = new Edge[m];
227+
228+
for (int i = 0; i < m; i++) {
229+
int from = in.nextInt();
230+
int to = in.nextInt();
231+
int weight = in.nextInt();
232+
233+
edges[i] = new Edge(from, to, weight);
234+
}
235+
236+
long answer = solve(n, edges);
237+
238+
if (answer == -1) {
239+
out.writeString(FAIL);
240+
} else {
241+
out.writeLong(answer);
242+
}
243+
244+
out.writeByte('\n');
245+
out.flush();
246+
}
247+
248+
private static void test() {
249+
assertEq(19, solve(4, new Edge[]{
250+
new Edge(1, 2, 5),
251+
new Edge(1, 3, 6),
252+
new Edge(2, 4, 8),
253+
new Edge(3, 4, 3)
254+
}));
255+
256+
assertEq(3, solve(3, new Edge[]{
257+
new Edge(1, 2, 1),
258+
new Edge(1, 2, 2),
259+
new Edge(2, 3, 1)
260+
}));
261+
262+
assertEq(-1, solve(2, new Edge[]{}));
263+
264+
assertEq(0, solve(1, new Edge[]{}));
265+
266+
assertEq(10, solve(2, new Edge[]{
267+
new Edge(1, 1, 100),
268+
new Edge(1, 2, 10),
269+
new Edge(2, 2, 100)
270+
}));
271+
272+
assertEq(0, solve(3, new Edge[]{
273+
new Edge(1, 2, 0),
274+
new Edge(2, 3, 0)
275+
}));
276+
277+
System.out.println("Test OK");
278+
}
279+
280+
static void assertEq(long exp, long act) {
281+
if (exp != act) {
282+
throw new AssertionError("Expected=" + exp + ", actual=" + act);
283+
}
284+
}
285+
286+
public static void main(String[] args) throws Exception {
287+
if (System.getProperty("os.name").startsWith("Windows")) {
288+
test();
289+
} else {
290+
run();
291+
}
292+
}
293+
}

0 commit comments

Comments
 (0)