Moocast
Solución en video
Por Hannah Ying
Nota: La solución en video podría no ser la misma que las otras soluciones. Código en Java.
Video de YouTube (DpIUSBBhNFU)
Solución 1 (DSU)
Explicación
Podemos construir aristas entre cada par de vacas. Tras ordenar las aristas por peso podemos usar un DSU. Así nos aseguramos de incluir cada vaca solo una vez a través de los pesos más pequeños posibles.
Implementación
Complejidad temporal:
#include <algorithm>
#include <fstream>
#include <iostream>
#include <vector>
using std::cout;
using std::endl;
using std::vector;
// BeginCodeSnip{DSU}
class DSU {
private:
vector<int> parents;
vector<int> sizes;
public:
DSU(int size) : parents(size), sizes(size, 1) {
for (int i = 0; i < size; i++) { parents[i] = i; }
}
int get_top(int n) {
return parents[n] == n ? n : (parents[n] = get_top(parents[n]));
}
bool link(int n1, int n2) {
n1 = get_top(n1);
n2 = get_top(n2);
if (n1 == n2) { return false; }
if (sizes[n1] < sizes[n2]) { std::swap(n1, n2); }
sizes[n1] += sizes[n2];
parents[n2] = n1;
return true;
}
};
// EndCodeSnip
struct Edge {
int a, b, dist;
};
int main() {
std::ifstream read("moocast.in");
int n;
read >> n;
vector<int> x(n);
vector<int> y(n);
for (int i = 0; i < n; i++) { read >> x[i] >> y[i]; }
// Generamos distancias entre todos los pares de vacas.
vector<Edge> edges;
for (int i = 0; i < n; i++) {
for (int j = i + 1; j < n; j++) {
int dx = x[i] - x[j];
int dy = y[i] - y[j];
edges.push_back({i, j, dx * dx + dy * dy});
}
}
// Ordenamos las aristas por su distancia.
auto cmp = [](const Edge &e1, const Edge &e2) { return e1.dist < e2.dist; };
std::sort(edges.begin(), edges.end(), cmp);
// Agregamos aristas de la distancia más pequeña a la más grande hasta que el grafo
// quede conexo. La respuesta es la última arista agregada.
int last_dist = 0;
int comp_num = n;
DSU dsu(n);
for (const Edge &e : edges) {
if (dsu.link(e.a, e.b)) {
last_dist = e.dist;
if (--comp_num == 1) { break; }
}
}
std::ofstream("moocast.out") << last_dist << endl;
}import java.io.*;
import java.util.*;
public class MooCast {
static class Edge {
public int a;
public int b;
public int dist;
public Edge(int a, int b, int dist) {
this.a = a;
this.b = b;
this.dist = dist;
}
}
public static void main(String[] args) throws IOException {
BufferedReader read = new BufferedReader(new FileReader("moocast.in"));
int n = Integer.parseInt(read.readLine());
int[] x = new int[n];
int[] y = new int[n];
for (int i = 0; i < n; i++) {
StringTokenizer cow = new StringTokenizer(read.readLine());
x[i] = Integer.parseInt(cow.nextToken());
y[i] = Integer.parseInt(cow.nextToken());
}
// Generamos distancias entre todos los pares de vacas.
List<Edge> edges = new ArrayList<>();
for (int i = 0; i < n; i++) {
for (int j = i + 1; j < n; j++) {
int dx = x[i] - x[j];
int dy = y[i] - y[j];
edges.add(new Edge(i, j, dx * dx + dy * dy));
}
}
// Ordenamos las aristas por su distancia.
edges.sort(Comparator.comparingInt(e -> e.dist));
// Agregamos aristas de la distancia más pequeña a la más grande hasta que el grafo
// quede conexo. La respuesta es la última arista agregada.
int lastDist = 0;
int compNum = n;
DSU dsu = new DSU(n);
for (Edge e : edges) {
if (dsu.link(e.a, e.b)) {
lastDist = e.dist;
if (--compNum == 1) { break; }
}
}
PrintWriter written = new PrintWriter("moocast.out");
written.println(lastDist);
written.close();
}
}
// BeginCodeSnip{DSU}
class DSU {
private final int[] parents;
private final int[] sizes;
public DSU(int size) {
parents = new int[size];
sizes = new int[size];
for (int i = 0; i < size; i++) {
parents[i] = i;
sizes[i] = 1;
}
}
public int getTop(int n) {
return parents[n] == n ? n : (parents[n] = getTop(parents[n]));
}
public boolean link(int e1, int e2) {
e1 = getTop(e1);
e2 = getTop(e2);
if (e1 == e2) { return false; }
if (sizes[e2] > sizes[e1]) { return link(e2, e1); }
parents[e2] = e1;
sizes[e1] += sizes[e2];
return true;
}
}
// EndCodeSnipfrom typing import NamedTuple
# BeginCodeSnip{DSU}
class DSU:
def __init__(self, size: int) -> None:
self.sizes = [1 for _ in range(size)]
self.parents = [i for i in range(size)]
def get_top(self, n: int) -> int:
if self.parents[n] == n:
return n
self.parents[n] = self.get_top(self.parents[n])
return self.parents[n]
def link(self, n1: int, n2: int) -> bool:
n1 = self.get_top(n1)
n2 = self.get_top(n2)
if n1 == n2:
return False
if self.sizes[n1] < self.sizes[n2]:
n1, n2 = n2, n1
self.sizes[n1] += self.sizes[n2]
self.parents[n2] = n1
return True
# EndCodeSnip
class Edge(NamedTuple):
a: int
b: int
dist: int
with open("moocast.in") as read:
n = int(read.readline())
x = []
y = []
for i in range(n):
x_i, y_i = [int(i) for i in read.readline().split()]
x.append(x_i)
y.append(y_i)
# Generamos distancias entre todos los pares de vacas.
edges = []
for i in range(n):
for j in range(i + 1, n):
dx = x[i] - x[j]
dy = y[i] - y[j]
edges.append(Edge(i, j, dx**2 + dy**2))
# Ordenamos las aristas por su distancia.
edges.sort(key=lambda e: e.dist)
# Agregamos aristas de la distancia más pequeña a la más grande hasta que el grafo
# quede conexo. La respuesta es la última arista agregada.
last_dist = 0
comp_num = n
dsu = DSU(n)
for e in edges:
if dsu.link(e.a, e.b):
last_dist = e.dist
comp_num -= 1
if comp_num == 1:
break
print(last_dist, file=open("moocast.out", "w"))Solución 2 (Prim)
Explicación
Podemos construir aristas entre cada par de vacas. Luego construimos un MST eligiendo vorazmente el nodo más cercano cada vez. Esto garantiza que incluimos cada vaca en la red usando los pesos más pequeños posibles.
Implementación
Complejidad temporal:
#include <bits/stdc++.h>
using namespace std;
int dist_sq(pair<int, int> &a, pair<int, int> &b) {
int dx = a.first - b.first;
int dy = a.second - b.second;
return dx * dx + dy * dy;
}
int main() {
freopen("moocast.in", "r", stdin);
int N;
cin >> N;
vector<pair<int, int>> cows(N);
for (auto &[x, y] : cows) { cin >> x >> y; }
/*
* distancia más corta a una vaca en el árbol de expansión
* o INT_MAX - 1 si no hay vacas en el árbol de expansión
* o INT_MAX si la vaca está en el árbol de expansión
*/
vector<int> dist(N, INT_MAX - 1);
dist[0] = 0;
int m = 0;
for (int t = 0; t < N; t++) {
int i = min_element(dist.begin(), dist.end()) - dist.begin();
m = max(m, dist[i]);
dist[i] = INT_MAX;
for (int j = 0; j < N; j++) {
if (dist[j] != INT_MAX) {
dist[j] = min(dist[j], dist_sq(cows[i], cows[j]));
}
}
}
freopen("moocast.out", "w", stdout);
cout << m << endl;
}import java.io.*;
import java.util.*;
public class MooCast {
static int distSq(int[] a, int[] b) {
int dx = a[0] - b[0];
int dy = a[1] - b[1];
return dx * dx + dy * dy;
}
static int minIndex(int[] a) {
int m = a[0];
int j = 0;
for (int i = 1; i < a.length; i++) {
if (a[i] < m) {
m = a[i];
j = i;
}
}
return j;
}
public static void main(String[] args) throws IOException {
BufferedReader r = new BufferedReader(new FileReader("moocast.in"));
StringTokenizer st = new StringTokenizer(r.readLine());
int N = Integer.parseInt(st.nextToken());
int[][] cows = new int[N][2];
for (int i = 0; i < N; i++) {
st = new StringTokenizer(r.readLine());
cows[i][0] = Integer.parseInt(st.nextToken());
cows[i][1] = Integer.parseInt(st.nextToken());
}
/*
* distancia más corta a una vaca en el árbol de expansión
* o Integer.MAX_VALUE - 1 si no hay vacas en el árbol de expansión
* o Integer.MAX_VALUE si la vaca está en el árbol de expansión
*/
int[] dist = new int[N];
for (int i = 1; i < N; i++) { dist[i] = Integer.MAX_VALUE - 1; }
int m = 0;
for (int t = 0; t < N; t++) {
int i = minIndex(dist);
m = Math.max(m, dist[i]);
dist[i] = Integer.MAX_VALUE;
for (int j = 0; j < N; j++) {
if (dist[j] != Integer.MAX_VALUE) {
dist[j] = Math.min(dist[j], distSq(cows[i], cows[j]));
}
}
}
PrintWriter pw = new PrintWriter(new FileWriter("moocast.out"));
pw.println(m);
pw.close();
}
}def dist_sq(a, b):
return (a[0] - b[0]) ** 2 + (a[1] - b[1]) ** 2
with open("moocast.in", "r") as stdin:
N = int(stdin.readline())
cows = [tuple(map(int, stdin.readline().strip().split())) for _ in range(N)]
dist = {c: float("inf") for c in cows}
dist[cows[0]] = 0
m = 0
for t in range(N):
i = min(dist, key=dist.__getitem__)
m = max(m, dist[i])
del dist[i]
for j in dist:
dist[j] = min(dist[j], dist_sq(i, j))
print(m, file=open("moocast.out", "w"))Explicación (búsqueda binaria + BFS)
Una solución alternativa usa búsqueda binaria y BFS.
Definamos una función que comprueba si todas las vacas son alcanzables gastando un máximo de power por walkie-talkie.
Esto se puede lograr con BFS.
Notemos que esta función es monótona: si todas las vacas son alcanzables con un cierto nivel de potencia , seguirán siéndolo con cualquier nivel de potencia mayor que .
Por lo tanto, se puede emplear búsqueda binaria para hallar el valor mínimo de power para el cual all_reachable(power) es verdadero.
Implementación
Complejidad temporal: , donde
#include <fstream>
#include <iostream>
#include <queue>
#include <vector>
using std::cout;
using std::endl;
using std::pair;
using std::queue;
using std::vector;
/** @return la distancia al cuadrado entre 2 puntos */
int dist_sq(const pair<int, int> &p1, const pair<int, int> &p2) {
int dx = p1.first - p2.first;
int dy = p1.second - p2.second;
return dx * dx + dy * dy;
}
/** @return si todas las vacas son alcanzables con el nivel de potencia dado */
bool all_reachable(int power, const vector<pair<int, int>> &cows) {
int start = 0;
queue<int> frontier;
frontier.push(start);
vector<bool> reached(cows.size());
reached[start] = true;
while (!frontier.empty()) {
int curr = frontier.front();
frontier.pop();
for (int c = 0; c < cows.size(); c++) {
if (!reached[c] && dist_sq(cows[curr], cows[c]) <= power) {
frontier.push(c);
reached[c] = true;
}
}
}
for (bool c : reached) {
if (!c) { return false; }
}
return true;
}
int main() {
std::ifstream read("moocast.in");
int cow_num;
read >> cow_num;
vector<pair<int, int>> cows(cow_num);
for (pair<int, int> &c : cows) { read >> c.first >> c.second; }
// Búsqueda binaria para hallar el nivel de potencia mínimo necesario
int lo = 0;
int hi = INT_MAX;
int valid = -1;
while (lo <= hi) {
int mid = lo + (hi - lo) / 2;
if (all_reachable(mid, cows)) {
valid = mid;
hi = mid - 1;
} else {
lo = mid + 1;
}
}
std::ofstream("moocast.out") << valid << endl;
}import java.io.*;
import java.util.*;
public class Moocast {
public static void main(String[] args) throws IOException {
BufferedReader read = new BufferedReader(new FileReader("moocast.in"));
int cowNum = Integer.parseInt(read.readLine());
int[][] cows = new int[cowNum][2];
for (int c = 0; c < cowNum; c++) {
StringTokenizer cow = new StringTokenizer(read.readLine());
cows[c][0] = Integer.parseInt(cow.nextToken());
cows[c][1] = Integer.parseInt(cow.nextToken());
}
// Búsqueda binaria para hallar el nivel de potencia mínimo necesario
int lo = 0;
int hi = Integer.MAX_VALUE;
int valid = -1;
while (lo <= hi) {
int mid = lo + (hi - lo) / 2;
if (allReachable(mid, cows)) {
valid = mid;
hi = mid - 1;
} else {
lo = mid + 1;
}
}
PrintWriter written = new PrintWriter("moocast.out");
written.println(valid);
written.close();
}
// Función para comprobar si todas las vacas son alcanzables dado un cierto nivel de potencia
static boolean allReachable(int power, int[][] cows) {
int start = 0;
ArrayDeque<Integer> frontier = new ArrayDeque<>();
frontier.add(start);
boolean[] reached = new boolean[cows.length];
reached[start] = true;
while (!frontier.isEmpty()) {
int curr = frontier.poll();
for (int c = 0; c < cows.length; c++) {
if (!reached[c] && distSq(cows[curr], cows[c]) <= power) {
frontier.add(c);
reached[c] = true;
}
}
}
for (boolean c : reached) {
if (!c) { return false; }
}
return true;
}
/** @return la distancia al cuadrado entre dos puntos */
static int distSq(int[] p1, int[] p2) {
int dx = p1[0] - p2[0];
int dy = p1[1] - p2[1];
return dx * dx + dy * dy;
}
}from collections import deque
def build_adj(cows):
n = len(cows)
adj = [[] for _ in range(n)]
max_dist = 0
for i, (xi, yi) in enumerate(cows):
neighbors = []
for j, (xj, yj) in enumerate(cows):
if i == j:
continue
dx = xi - xj
dy = yi - yj
d = dx * dx + dy * dy
neighbors.append((d, j))
if d > max_dist:
max_dist = d
neighbors.sort()
adj[i] = neighbors
return adj, max_dist
def all_reachable(power, adj):
n = len(adj)
reached = [False] * n
reached[0] = True
q = deque([0])
append = q.append
popleft = q.popleft
while q:
curr = popleft()
for d, j in adj[curr]:
if d > power:
break
if not reached[j]:
reached[j] = True
append(j)
return all(reached)
with open("moocast.in") as f:
n = int(f.readline().strip())
cows = [tuple(map(int, f.readline().split())) for _ in range(n)]
adj, max_dist = build_adj(cows)
lo, hi = 0, max_dist
valid = hi
while lo <= hi:
mid = (lo + hi) // 2
if all_reachable(mid, adj):
valid = mid
hi = mid - 1
else:
lo = mid + 1
with open("moocast.out", "w") as f:
f.write(str(valid) + "\n")