Cómo resolver el problema de ‘cortar el árbol’ en HackerRank

Este problema es una aplicación simple de recursión y profundidad del primer recorrido de un árbol.
Debe resolver este problema utilizando la técnica transversal de orden posterior, considerando el árbol como un árbol enraizado .


Tomemos el ejemplo dado en la pregunta.


Considere el nodo 1 como la raíz del árbol. Comenzamos dfs desde la raíz (nodo 1). Tan pronto como el control llega al nodo 4, comienza el retroceso. Este es el momento en el que necesitamos calcular el Tree_diff considerando el borde 5—4.
Luego, el control llega al nodo 5 y agregamos el valor del nodo 4 (500) al valor del nodo 5. Ahora el valor del nodo 5 es 600. De manera similar, después de retroceder desde el nodo 6, el valor del nodo 5 será (1200). Ahora, hemos terminado con los hijos del nodo 5. Entonces, calculamos el Tree_diff considerando el borde 2—5.

Nota: Si bien la parte de retroceso de la Profundidad del primer recorrido, cada nodo contendrá la suma del valor de todos sus elementos secundarios, incluido él mismo. Cuando terminamos con todos los hijos de un nodo, tenemos dos subárboles ( T1 = Subárbol que contiene el nodo actual y sus hijos, T2 = Subárbol que contiene el padre del nodo actual y el resto del nodo).

De esa manera imprimimos la diferencia mínima como respuesta.

void dfs (int u, int p) {
for (int v : adj [u]) {
if (v != p) {
dfs (v, u);
value [u] += value [v];
}
}
if (u != 1) {
ans = min (ans, abs (total - 2 * value [u]));
}
}

Fácil. Hay una variedad de problemas con este concepto donde la suma se reemplaza por XOR, OR, AND, producto, etc.

Considere el peso total del árbol sea W y también el peso de un subárbol enraizado en U sea A. Ahora, si eliminamos el borde entre A y el padre (A). El peso de la otra parte del gráfico será WA.

Por lo tanto, necesita encontrar el valor mínimo de abs (A, WA). Puede considerar cada subárbol por una sola llamada DFS. Entonces, la complejidad del tiempo será O (V). Aquí está el código en C ++.

#include
usando el espacio de nombres estándar;
vector * árbol;
vector
treedata;
int n, u, v, total, ans;
int dfs (int u) {
int a continuación = treedata [u];
datos de árbol [u] = -1;
for (int i = 0; i if (treedata [árbol [u] [i]]! = -1)
debajo de + = dfs (árbol [u] [i]);
}
if (abs (total – (2 * abajo)) ans = abs (total – (2 * abajo));
}
volver a continuación;
}
int main () {
cin >> n;
árbol = nuevo vector [n + 1];
treedata.resize (n + 1);
total = 0;
ans = 1e9;
para (int i = 1; i cin >> treedata [i];
total + = datos de árbol [i];
}
para (int i = 1; i cin >> u >> v;
árbol [u] .push_back (v);
árbol [v] .push_back (u);
}
dfs (1);
cout << ans << endl;
devuelve 0;
}

Estaba buscando las respuestas y encuentro que la mayoría de las soluciones de esta pregunta son bastante complicadas para que un ser humano las entienda. Entonces decidí buscar otra solución. La primera solución fue la recursividad con complejidad de tiempo O (n), sin embargo, terminó teniendo el problema StackOverflow . Luego decidió resolverlo con estructuras de datos Graph y Stack. Esta solución pasa de las pruebas de rendimiento en hackerrank y la complejidad del tiempo es O (n).

¿Por qué no el árbol binario porque es más fácil mantener la poligonal cuando los nodos NO se dan en orden padre-hijo como está en la pregunta y no es necesario que le digan que el primer nodo es el nodo raíz :). La otra ventaja es que la solución a continuación funcionará cuando el árbol NO sea un árbol binario 🙂

Primero, crea el gráfico y llénalo con sus conexiones. Al mismo tiempo, mantenga la suma total de la gráfica mientras agrega los vértices.

Gráfico de clase pública {
Nodo [] nodos;
int totalWeight = 0;
Graph (int size, int data []) {
nodos = nuevo nodo [tamaño];
int cuenta = 0;
while (cuenta nodos [recuento] = nuevo nodo (datos [recuento], recuento + 1);
totalWeight + = datos [recuento];
recuento ++;
}
}
Nodo de clase {
Private int idx;
datos int privados;
Conexiones LinkedList = new LinkedList <> ();
booleano visitado;
privado int totalWeight;
Nodo público (datos int, int idx) {
this.data = datos;
this.idx = idx;
this.totalWeight = data;
}
}

luego creemos una función en la clase Graph para agregar los nodos al gráfico. La razón por la que restamos 1 ya que las matrices comienzan desde 0 y en la pregunta, comienzan desde 1. Básicamente, simplemente agregamos las conexiones entre sí.

addNode público vacío (int firstNode, int secondNode) {
nodos [primerNodo-1]. connections.add (nodos [segundoNodo-1]);
nodos [secondNode-1]. connections.add (nodos [firstNode-1]);
}

Ahora escribamos nuestra función para encontrar la diferencia mínima. Primero, podemos agregar todos los elementos de nodo a una pila iterando a través de las conexiones (línea 2–17). Lo importante es marcar los elementos visitados como falsos para que no tengamos un ciclo de iteración sin fin.

Luego, en la misma función, comenzamos a aparecer desde la pila. Antes de hacerlo, restableceremos los nodos con falso nuevamente (la otra opción también es verificar si se visitó al revés para que no tenga que restablecer nuevamente). En cada iteración, agregaremos el peso total del nodo emergente a poppedNode.connections totalWeight uno por uno.

A continuación, en la misma iteración del elemento de pila, intentaremos encontrar la diferencia restando (wholeTreeSum – currentNodesSum) de currentNodesSum, que también significa | wholeTreeSum-2 * currentNodeSum |

El | significa absoluto en matemáticas.

Lo único que queda ahora es almacenar la diferencia mínima hasta ahora en la memoria de la función.

A continuación se muestra toda la función finMindifByStack.

void findMinDifByStack () {
Nodo raíz = nodos [0];
Pila s = nueva Pila ();
s.push (raíz);
root.visited = true;
LinkedList
connections = root.connections;
while (connections.size ()> 0) {
LinkedList newConnections = new LinkedList <> ();
para (Conexión de nodo: conexiones) {
if (! connection.visited) {
s.push (conexión);
connection.visited = true;
newConnections.addAll (connection. connections);
}
}
conexiones = nuevas conexiones;
}
Reiniciar();
int currentMinDiff = Integer.MAX_VALUE;
while (! s.isEmpty ()) {
Nodo nodo = s.pop ();
nodo.visitado = verdadero;
for (Conexión de nodo: node. connections) {
if (! connection.visited) {
connection.totalWeight = connection.totalWeight + node.totalWeight;
}
}
int newDiff = Math.abs (totalWeight – 2 * node.totalWeight);
if (newDiff currentMinDiff = newDiff;
}
}

System.out.println (currentMinDiff);
}

La parte del controlador es la siguiente. No use la clase Scanner, de lo contrario tendrá problemas de rendimiento.

public static void main (String [] args) lanza Exception {
BufferedReader in = new BufferedReader (nuevo InputStreamReader (Recursos e información del sistema));
int cuenta = Integer.valueOf (in.readLine ());
int [] números = Stream.of (in.readLine (). split (“”)). mapToInt (Integer :: parseInt) .toArray ();
Graph graph = new Graph (conteo, números);
contar-;
while (cuenta–> 0) {
int [] par = Stream.of (in.readLine (). split (“”)). mapToInt (Integer :: parseInt) .toArray ();
graph.addNode (par [0], par [1]);
}
graph.findMinDifByStack ();
}