Visits
Explicación
Como cada vaca solo quiere visitar a una de las otras vacas , podemos interpretar la entrada como un grafo funcional con aristas . El grafo resultante no es necesariamente conexo, así que hay que hallar todas las componentes conexas de este grafo y encontrar la cantidad máxima de “moos” de cada una por separado.
Para cada componente conexa del grafo funcional, observemos que debe haber uno y solo un ciclo. Además, todas las vacas pueden visitar a sus compañeras antes de irse excepto una, que quiere visitar a la vaca que inicia la cadena de visitas del ciclo. Para los demás caminos que llevan al ciclo, siempre podemos visitarlos primero antes de procesar el ciclo, así que siempre podrán hacer “moo”. Por tanto, la cantidad máxima de “moos” es la suma de todos los de la componente conexa. Hay que restar de la suma el mínimo del ciclo porque representa a la vaca anterior a la que inicia, que no podrá hacer moo.
Para cada vaca, primero comprobamos si ya está visitada. Si no, queremos hallar todas las demás vacas de esta componente conexa usando un grafo invertido y sumando los “moos”. Después, ejecutamos el algoritmo de Floyd para determinar el ciclo y hallar el valor mínimo de “v” en él.
Implementación
Complejidad temporal:
#include <bits/stdc++.h>
using namespace std;
// la vaca i quiere visitar a la vaca a[i] y obtiene v[i] puntos
vector<int> a, v;
// reversed_graph[i] guarda las vacas que quieren ir a la granja i
vector<vector<int>> reversed_graph;
// marca las vacas como visitadas una vez que las hemos procesado
vector<bool> visited;
/**
* Marca y y las demás vacas de su otro ciclo como visitadas haciendo una dfs.
*/
void mark(int y) {
if (visited[y]) { return; }
visited[y] = true;
for (int c : reversed_graph[y]) { mark(c); }
}
/**
* Aplica el algoritmo de Floyd para detectar el ciclo y devolver el valor
* mínimo de v en este ciclo.
*/
int min_in_cycle(int curr) {
int y = a[curr];
int z = a[y];
while (y != z) {
y = a[y];
z = a[a[z]];
}
// y es ahora un elemento que está en el ciclo
int min_v = v[y];
y = a[y];
// recorrer el ciclo para hallar la vaca con el valor mínimo de v_i
while (y != z) {
min_v = min(min_v, v[y]);
y = a[y];
}
// marcar todos los nodos de esta componente conexa como visitados
mark(y);
return min_v;
}
int main() {
int n;
cin >> n;
v.resize(n);
a.resize(n);
visited.resize(n);
reversed_graph.resize(n);
long long max_moos = 0;
for (int i = 0; i < n; i++) {
cin >> a[i] >> v[i];
a[i]--;
// la vaca i quiere visitar la granja a[i]
reversed_graph[a[i]].push_back(i);
max_moos += v[i];
}
for (int i = 0; i < n; i++) {
/*
* Para cada componente conexa no visitada con exactamente un ciclo, se
* puede visitar a todas excepto una vaca. Hacemos que esta vaca sea la
* de menor v_i y la restamos.
*/
if (!visited[i]) { max_moos -= min_in_cycle(i); }
}
cout << max_moos << endl;
}import java.io.*;
import java.util.*;
public class Visits {
// la vaca i quiere visitar a la vaca a[i] y obtiene v[i] puntos
static List<Integer> a, v;
// reversed_graph[i] guarda las vacas que quieren ir a la granja i
static List<List<Integer>> reversed_graph;
// marca las vacas como visitadas una vez que las hemos procesado
static List<Boolean> visited;
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
int n = Integer.parseInt(br.readLine());
a = new ArrayList<>(n);
v = new ArrayList<>(n);
reversed_graph = new ArrayList<>(n);
for (int i = 0; i < n; i++) { reversed_graph.add(new ArrayList<>()); }
visited = new ArrayList<>();
long maxMoos = 0;
for (int i = 0; i < n; i++) {
StringTokenizer st = new StringTokenizer(br.readLine());
a.add(Integer.parseInt(st.nextToken()) - 1);
v.add(Integer.parseInt(st.nextToken()));
// la vaca i quiere visitar la granja a[i]
reversed_graph.get(a.get(i)).add(i);
visited.add(false);
maxMoos += v.get(i);
}
for (int i = 0; i < n; i++) {
/*
* Para cada componente conexa no visitada con exactamente un ciclo,
* se puede visitar a todas excepto una vaca. Hacemos que esta vaca
* sea la de menor v_i y la restamos.
*/
if (!visited.get(i)) { maxMoos -= minInCycle(i); }
}
System.out.println(maxMoos);
}
/**
* Aplica el algoritmo de Floyd para detectar el ciclo y devolver el valor
* mínimo de v en este ciclo que contiene al nodo.
*/
static int minInCycle(int node) {
int slow = a.get(node);
int quick = a.get(slow);
while (slow != quick) {
slow = a.get(slow);
quick = a.get(a.get(quick));
}
// slow es ahora un elemento que está en el ciclo
int min_v = v.get(slow);
// recorrer el ciclo para hallar la vaca con el valor mínimo de v_i
slow = a.get(slow);
while (slow != quick) {
min_v = Math.min(min_v, v.get(slow));
slow = a.get(slow);
}
// marcar todos los nodos de esta componente conexa como visitados
mark(slow);
return min_v;
}
/**
* Marca y y las demás vacas de su otro ciclo como visitadas haciendo una
* dfs.
*/
static void mark(int node) {
if (visited.get(node)) { return; }
visited.set(node, true);
for (Integer child : reversed_graph.get(node)) { mark(child); }
}
}