Algorithmic Solutions: Python and MiniZinc Code Snippets
Iterador de Fuerza Bruta (Base N)
Implementación de un iterador genérico para generar combinaciones en una base N, esencial para problemas de búsqueda exhaustiva como Knapsack o la generación de permutaciones.
def next_number(digits, base):
next_digits = digits.copy()
carry = 1
for digit in range(len(next_digits) - 1, -1, -1):
next_val = next_digits[digit] + carry
carry = next_val // base
if carry == 0:
next_digits[digit] = next_val
return next_digits
next_digits[digit] = 0
return next_digits
class My_Iterator:
def __init__(self, num_digits, base):
self.num_digits = num_digits
self.base = base
def next(self):
d = [0] * (self.num_digits - 1) + [-1]
max_combination = [self.base - 1] * self.num_digits
while d != max_combination:
d = next_number(d, self.base)
yield d
return
Problema de la Mochila (Fuerza Bruta)
Solución al problema 0/1 Knapsack utilizando el iterador de fuerza bruta para generar todas las posibles combinaciones de inclusión de ítems.
from my_iterator import *
def solve(capacity, input_list):
best_value = 0
solution = []
items = []
# Parsear la lista de entrada (valor, peso)
for item in input_list:
value, weight = map(int, item.split())
items.append((value, weight))
num_items = len(items)
# Base 2: 0 (no tomar) o 1 (tomar)
it = My_Iterator(num_items, 2)
for combination in it.next():
total_weight = 0
total_value = 0
chosen_items = []
for i, take in enumerate(combination):
if take == 1:
value, weight = items[i]
total_weight += weight
total_value += value
chosen_items.append(i + 1)
# Verificar si es una solución válida y mejor
if total_weight <= capacity and total_value > best_value:
best_value = total_value
solution = chosen_items
return best_value, solution
Ordenamiento Topológico (Fuerza Bruta)
Encuentra todos los ordenamientos topológicos válidos de un grafo dirigido, verificando cada permutación posible de nodos.
from my_iterator import *
import networkx as nx
from itertools import permutations
def solve(input_list):
solutions_list = []
# Construir el grafo a partir de la lista de aristas
G = nx.DiGraph()
max_node = 0
edges = []
for line in input_list:
u, v = map(int, line.split())
edges.append((u, v))
max_node = max(max_node, u, v)
num_nodes = max_node + 1
nodes = list(range(num_nodes))
# Usar fuerza bruta para generar todas las permutaciones posibles
for digits in permutations(nodes):
G.clear()
G.add_nodes_from(nodes)
G.add_edges_from(edges)
# Verificar si la permutación actual es un orden topológico válido
pos = {node: i for i, node in enumerate(digits)}
valid = all(pos[u] < pos[v] for u, v in edges)
if valid:
solutions_list.append(list(digits))
return len(solutions_list), solutions_list
Iterador con Dominios Concretos
Clase iteradora que genera combinaciones donde cada dígito tiene su propio conjunto de valores permitidos (dominios).
class My_Iterator:
def __init__(self, num_digits, digit_values):
self.num_digits = num_digits
self.digit_values = digit_values
def next(self):
# Creamos un vector de índices para recorrer los dominios
indices = [0] * self.num_digits
while True:
# Generamos la combinación actual
current = [self.digit_values[i][indices[i]] for i in range(self.num_digits)]
yield current
# Incrementamos los índices tipo "contador"
for i in reversed(range(self.num_digits)):
indices[i] += 1
if indices[i] < len(self.digit_values[i]):
break
indices[i] = 0
else:
# Si hemos recorrido todas las combinaciones, terminamos
return
Componentes Conectados (BFS)
Algoritmo de Búsqueda en Amplitud (BFS) para identificar y listar los componentes conectados de un grafo.
import networkx as nx
from simple_queue import Queue
def solve(graph):
visited = set()
components = []
for node in graph.nodes:
if node not in visited:
queue = Queue()
queue.enqueue(node)
component = []
while not queue.isEmpty():
current = queue.dequeue()
if current not in visited:
visited.add(current)
component.append(current)
for neighbor in graph.neighbors(current):
if neighbor not in visited:
queue.enqueue(neighbor)
components.append(component)
return components
Devolución del Cambio (DFS)
Uso de Búsqueda en Profundidad (DFS) para encontrar la combinación de monedas que minimiza la cantidad total de monedas para un cambio dado.
from sys import maxsize as infinite
def solve(coins, change):
solutions_list = []
min_length = [infinite] # Lista para permitir acceso/modificación desde inner function
def dfs(index, current_sum, current_path):
# Caso base: solución válida
if current_sum == change:
if len(current_path) < min_length[0]:
min_length[0] = len(current_path)
solutions_list.clear()
solutions_list.append(current_path.copy())
elif len(current_path) == min_length[0]:
solutions_list.append(current_path.copy())
return
# Si superamos el cambio, terminamos esa rama
if current_sum > change:
return
# Explorar todas las monedas (se permite reutilizar la misma moneda)
for i in range(index, len(coins)):
dfs(i, current_sum + coins[i], current_path + [i + 1]) # Guardamos el índice como enunciado
dfs(0, 0, [])
if not solutions_list:
return None
return solutions_list
Árbol de Expansión Mínimo (Kruskal)
Implementación del algoritmo de Kruskal para encontrar el Árbol de Expansión Mínimo (MST), utilizando DFS para la detección de ciclos.
import minigraph as nx
def check_cycle(tree, new_edge):
start, end = new_edge[0], new_edge[1]
visited = set()
def dfs(node):
if node == end:
return True
visited.add(node)
for neighbor in tree.neighbors(node):
if neighbor not in visited:
if dfs(neighbor):
return True
return False
return dfs(start)
def solve(input_list):
tree_edges_list = []
edges = []
# Leer y almacenar todas las aristas con su peso
for line in input_list:
u, v, w = line.split()
edges.append((u, v, int(w)))
edges.sort(key=lambda x: x[2])
tree = nx.Graph() # Inicializar grafo vacío (árbol)
for u, v, _ in edges:
tree.add_node(u)
tree.add_node(v)
for edge in edges:
# Si agregar la arista no crea un ciclo
if not check_cycle(tree, edge):
tree_edges_list.append(edge)
tree.add_edge(edge[0], edge[1], weight=edge[2])
return tree_edges_list
Coloreado de Grafos (Algoritmo RLF)
Algoritmo heurístico Recursive Largest First (RLF) para la coloración de grafos, priorizando nodos con mayor grado y aquellos que restringen más a los nodos no coloreados.
import minigraph as nx
def rlf_graph_coloring(graph):
nodes = set(graph.nodes())
colors = {}
color = 0
while nodes:
# 1. Elegir el nodo v con el mayor grado en el subgrafo no coloreado
v = max(nodes, key=lambda x: graph.degree(x))
color_class = {v}
forbidden = set(graph.neighbors(v))
nodes.remove(v)
while True:
# Candidatos: nodos no coloreados y no adyacentes a ningún nodo en color_class
candidates = [n for n in nodes if n not in forbidden]
if not candidates:
break
# 2. Elegir el candidato u que tenga el mayor número de vecinos en 'forbidden'
u = max(
candidates,
key=lambda n: (
len(set(graph.neighbors(n)) & forbidden),
graph.degree(n),
-n # si hay empate, toma menor índice
)
)
color_class.add(u)
nodes.remove(u)
forbidden.update(graph.neighbors(u))
for n in color_class:
colors[n] = color
color += 1
sorted_dict = {k: colors[k] for k in sorted(colors)}
return list(sorted_dict.values())
Distancia Mínima (Memoization)
Cálculo de la distancia mínima en un grafo ponderado utilizando programación dinámica con memoización (similar a Bellman-Ford o Dijkstra recursivo).
import minigraph as nx
def solve(graph, from_node, to_node):
memo = {}
parent = {}
def dist(v):
if v == from_node:
return 0
if v in memo:
return memo[v]
min_cost = float('inf')
best_u = None
for u in graph.predecessors(v):
edges = graph.edges(u, data=True)
weight = None
for src, dst, data in edges:
if dst == v:
weight = data.get('weight', 0)
break
if weight is None:
continue # No hay arista válida
d = dist(u) + weight
if d < min_cost:
min_cost = d
best_u = u
memo[v] = min_cost
parent[v] = best_u
return min_cost
min_distance = dist(to_node)
# Reconstrucción del camino
if min_distance == float('inf'):
return float('inf'), []
path = []
current = to_node
while current != from_node:
path.append(current)
current = parent[current]
path.append(from_node)
path.reverse()
return min_distance, path
Camino Mínimo (BFS)
Algoritmo de Búsqueda en Amplitud (BFS) para encontrar el camino más corto en términos de número de aristas (grafo no ponderado).
import networkx as nx
from simple_queue import *
def bfs_path(graph, first_node, last_node):
visited = set()
parent = {}
queue = Queue()
queue.enqueue(first_node)
visited.add(first_node)
parent[first_node] = None
while not queue.isEmpty():
current = queue.dequeue()
if current == last_node:
break
for neighbor in graph.neighbors(current):
if neighbor not in visited:
visited.add(neighbor)
parent[neighbor] = current
queue.enqueue(neighbor)
# Reconstruir la ruta desde last_node hacia first_node
route = []
current = last_node
if last_node not in parent:
return []
while current is not None:
route.append(current)
current = parent.get(current)
route.reverse()
return route
Función Partition (Quicksort)
Implementación del paso de partición utilizado en el algoritmo Quicksort, reordenando los elementos alrededor de un pivote.
def partition(items, left_pos, right_pos):
pivot = items[left_pos]
i = left_pos + 1
j = right_pos
while True:
# Avanzar i mientras el valor sea menor o igual al pivote
while i <= j and items[i] <= pivot:
i += 1
# Retroceder j mientras el valor sea mayor al pivote
while i <= j and items[j] > pivot:
j -= 1
# Si los índices se cruzan, salir
if i > j:
break
# Intercambiar los valores en i y j
items[i], items[j] = items[j], items[i]
# Finalmente, intercambiar el pivote con el elemento en j
items[left_pos], items[j] = items[j], items[left_pos]
return j
Programación Dinámica con Memoización
Implementación de una función recursiva con memoización para resolver un problema de programación dinámica definido por la relación de recurrencia t(p, q).
def solve(p, q):
# Creamos un diccionario para guardar subproblemas
memo = {}
def t(p, q):
if (p, q) in memo:
return memo[(p, q)]
if p == q:
memo[(p, q)] = 1
return 1
# Opción 1: tomar t(p+1, q)
op1 = t(p + 1, q)
# Opción 2: sumatoria desde k=p hasta k=q-1 de t(p,k) + t(k+1,q)
op2 = 0
for k in range(p, q):
op2 += t(p, k) + t(k + 1, q)
result = max(op1, op2)
memo[(p, q)] = result
return result
return t(p, q)
MiniZinc: Asignación de Trasplantes de Riñón
Modelo de optimización para maximizar la compatibilidad total en la asignación de donantes a receptores.
% Variables de decisión: asignar a cada donante un receptor
array[PARTICIPANTS] of var PARTICIPANTS: assignment;
% Todos los receptores deben estar emparejados una sola vez
constraint all_different(assignment);
% Se prohíben emparejamientos con 0% de compatibilidad
constraint
forall(i in PARTICIPANTS) (
compatibility[donor_categories[i], recipient_categories[assignment[i]]] > 0
);
% Función objetivo: maximizar la compatibilidad total
var int: total_compatibility =
sum(i in PARTICIPANTS)(
compatibility[donor_categories[i], recipient_categories[assignment[i]]]
);
solve maximize total_compatibility;
output [
"Compatibilidad total: ", show(total_compatibility), "\n",
"Emparejamiento: ", show(assignment)
];
MiniZinc: Dosis de Pesticidas (Restricciones)
Modelo para asignar dosis de pesticidas minimizando la suma total, sujeto a restricciones de diferencia y ordenamiento.
array[1..N] of var min_dosis..max_dosis: dosis;
constraint all_different(dosis);
% Restricción: No progresión aritmética de longitud 3
constraint forall(i in 1..N-2) (
2 * dosis[i+1] != dosis[i] + dosis[i+2]
);
% Restricción: Dosis crecientes en la segunda mitad
constraint forall(i in 1..N div 2, j in (N div 2 + 1)..N) (
dosis[i] < dosis[j]
);
solve minimize sum(dosis);
MiniZinc: Planificación de Ayuda Humanitaria
Modelo de scheduling que utiliza la restricción cumulative
y asegura que las tareas de alta prioridad se completen antes que las de baja prioridad.
array[1..n] of var 0..sum(durations) : start_times;
var int : total_time;
constraint alldifferent(start_times);
% Restricciones de recursos (camiones y transportistas)
constraint cumulative(start_times, durations, camiones, t);
constraint cumulative(start_times, durations, transportistas, k);
% Restricción de prioridad: Prioridad 1 debe empezar antes que Prioridad 2
constraint forall(e in 1..n where priorities[e] == 1) (
forall(e2 in 1..n where priorities[e2] == 2) (
start_times[e] < start_times[e2]
)
);
constraint total_time = max([start_times[i] + durations[i] | i in 1..n]);
solve minimize total_time;
MiniZinc: Flujo Máximo en Tuberías
Modelo de flujo máximo que incorpora restricciones de capacidad, conservación de flujo y rangos de presión en las tuberías.
% Restricción: el flujo no puede exceder la capacidad
constraint forall(e in 1..num_edges)(
flow[e] <= capacity[e]
);
% Restricción: presión en rango permitido
constraint forall(e in 1..num_edges)(
let {
var int: pressure = base_pressure[e] + k * flow[e]
} in
pressure >= pressure_range[e, 1] /\ pressure <= pressure_range[e, 2]
);
% Restricción: conservación del flujo para nodos intermedios
constraint forall(n in 1..num_nodes where n != source /\ n != sink)(
let {
set of int: in_edges = {e | e in 1..num_edges where edges[e, 2] == n},
set of int: out_edges = {e | e in 1..num_edges where edges[e, 1] == n}
} in
sum(e in in_edges)(flow[e]) = sum(e in out_edges)(flow[e])
);
% Restricción: definición del flujo total que llega al sumidero
constraint
max_flow = sum(e in 1..num_edges where edges[e, 2] == sink)(flow[e]);
MiniZinc: Flujo Máximo con Capacidad de Nodo
Extensión del modelo de flujo máximo que incluye restricciones de capacidad en los nodos (estaciones de bombeo).
% Conservación de flujo en nodos intermedios (estaciones de bombeo)
constraint forall(eb in 1..num_nodes where eb != source /\ eb != sink) (
sum(i in 1..num_edges where edges[i, 1] == eb) (flow[i]) ==
sum(j in 1..num_edges where edges[j, 2] == eb) (flow[j])
);
% Restricciones de capacidad de arista
constraint forall(t in 1..num_edges) (
flow[t] >= 0 /\
flow[t] <= capacity[t]
);
% Restricción de capacidad de nodo (si node_capacity[n] está definido)
constraint forall(n in 1..num_nodes where node_capacity[n] != -1) (
sum(i in 1..num_edges where edges[i, 2] == n) (flow[i]) <= node_capacity[n]
);
constraint max_flow = sum(i in 1..num_edges where edges[i, 2] == sink) (flow[i]);
solve maximize max_flow;
MiniZinc: Knapsack 0/1 (Variables Binarias)
Modelo clásico del problema de la mochila 0/1 utilizando variables binarias para decidir qué ítems incluir.
array[ITEMS] of var 0..1: x;
var int: total_value = sum(i in ITEMS)(x[i] * value[i]);
constraint sum(i in ITEMS)(x[i] * weight[i]) <= capacity;
solve maximize total_value;
output [
"taken = {",
concat([ if fix(x[i]) = 1 then show(i) ++ ", " else "" endif | i in ITEMS ]),
"}\n",
"Total Value = ", show(total_value), "\n",
"----------\n=========="
];
MiniZinc: Ladrón de Casas (Restricciones Adyacentes)
Modelo para maximizar el valor robado sin robar casas adyacentes y respetando un límite máximo de casas robadas (x).
array[1..n] of var 0..1: taken;
% No se pueden robar casas adyacentes
constraint forall(i in 1..n-1)(taken[i] + taken[i+1] <= 1);
% Límite máximo de casas robadas
constraint sum(i in 1..n)(taken[i]) <= x;
var int: total_value = sum(i in 1..n)(taken[i] * value[i]);
solve maximize total_value;
output [
"taken = ", show(taken), "\n",
"total_value = ", show(total_value), "\n",
"--------------\n=========="
];
MiniZinc: Planificación Temporal (Cumulative)
Modelo de scheduling que minimiza el tiempo total de finalización (makespan) utilizando la restricción cumulative
.
array[1..n] of var 0..sum(tiempo) : comienzo;
var 0..sum(tiempo) : total = max([comienzo[i] + tiempo[i] | i in 1..n]);
% prog: recursos requeridos por cada tarea
% k: capacidad total del recurso
constraint cumulative(comienzo, tiempo, prog, k);
solve minimize total;
MiniZinc: Reparto de Caramelos (Notas)
Modelo para distribuir caramelos basado en las notas de los estudiantes, asegurando que la cantidad de caramelos refleje las diferencias de nota.
% Asumiendo que 'x' es el array de caramelos asignados
% total_caramelos = sum(x); % Esto debería ser una definición de variable, no una restricción
% Restricción: La cantidad de caramelos de i es menor que el doble de la cantidad de j
constraint forall(i in 1..n, j in 1..n)(x[i] < 2*x[j]);
% Restricciones basadas en la comparación de notas adyacentes
constraint forall(i in 2..n)(
(nota[i] == nota[i-1] -> x[i] == x[i-1]) /\
(nota[i] > nota[i-1] -> x[i] > x[i-1]) /\
(nota[i] < nota[i-1] -> x[i] < x[i-1])
);
MiniZinc: Sudoku Solver
Modelo de satisfacción para resolver Sudokus utilizando la restricción alldifferent
en filas, columnas y bloques 3×3.
% R es el rango de filas/columnas (e.g., 1..9)
% Restricción: Todas las filas deben tener valores diferentes
constraint
forall(r in R) (
alldifferent([s[r, c] | c in R])
);
% Restricción: Todas las columnas deben tener valores diferentes
constraint
forall(c in R) (
alldifferent([s[r, c] | r in R])
);
% Restricción: Todos los bloques 3x3 deben tener valores diferentes
constraint
forall(sr in 0..2, sc in 0..2) (
alldifferent([s[3*sr + r, 3*sc + c] | r in 1..3, c in 1..3])
);
solve satisfy;
output [
show([s[r, c] | r in R, c in R])
];
MiniZinc: Knapsack 0/1 (Variables de Conjunto)
Modelo alternativo del problema de la mochila 0/1 utilizando una variable de conjunto para representar los ítems seleccionados.
var set of 1..n : taken;
var int : valor;
% Restricción de capacidad
constraint capacity >= sum(i in taken)(weight[i]);
% Definición del valor total
constraint valor = sum(i in taken) (value[i]);
solve maximize valor;
MiniZinc: Ubicación de Instalaciones
Modelo de optimización para minimizar el costo total de abrir instalaciones y asignar clientes a ellas.
var float: total_cost = sum(f in Facilities)(open[f] * installation_costs[f]) +
sum(c in Clients, f in Facilities)(transportation_costs[f, c] * assign[f, c]);
solve minimize(total_cost);
% Restricción: Si un cliente es asignado a una instalación, esta debe estar abierta
constraint forall(c in Clients, f in Facilities where assign[f, c] == 1)(open[f] == 1);
% Restricción: Cada cliente debe ser asignado a exactamente una instalación
constraint forall(c in Clients)(sum(f in Facilities)(assign[f,c]) == 1);
MiniZinc: TSP (Minimizar Arista Máxima)
Modelo del Problema del Viajante (TSP) que busca un circuito que minimice la longitud de la arista más larga del recorrido.
array[City] of var City: succ;
var int : maxEdge;
% Debe formar un circuito hamiltoniano
constraint circuit(succ);
% Restricciones de distancia y límite de arista
constraint forall(c in City)(
distance[c, succ[c]] != -1 /\
distance[c, succ[c]] <= maxAllowedEdge
);
% Definición de la arista máxima
constraint maxEdge = max(c in City)(distance[c, succ[c]]);
solve minimize maxEdge;
output [
"succ = \(succ)" ++
"\nmaxEdge = \(maxEdge)"
];
MiniZinc: Palillos (Criptoaritmética)
Modelo de satisfacción para resolver una ecuación criptoaritmética, donde las variables representan dígitos desconocidos.
% Asumiendo que 'x' es un array de variables de decisión que representan dígitos
constraint suma_palillos = sum(i in x)(palillos[i]);
constraint alldifferent(x);
% Ecuación criptoaritmética
constraint 2*1000+3*100+x[1]*10+3+x[2]*1000+9*100+8*10+x[3]==x[4]*1000+3*100+x[5]*10+1;
solve satisfy;
MiniZinc: Scheduling de Aerolíneas
Modelo de satisfacción para asignar vuelos a franjas horarias, respetando los límites de tiempo y la capacidad de cada franja.
constraint forall(i in Flights)(
assigned_time_slot[i] >= start_time[i] /\
assigned_time_slot[i] <= end_time[i]
);
constraint forall(i in TimeSlots)(
sum(j in Flights where assigned_time_slot[j] == i)(1) <= slot_capacity[i]
);
solve satisfy;
MiniZinc: Shop Scheduling (Minimizar Retraso)
Modelo de scheduling que busca minimizar el retraso total de las tareas respecto a sus fechas de vencimiento (due dates).
array[1..n] of var 0..sum(duration): start;
array[1..n] of int: resources = [1 | i in 1..n];
array[1..n] of var int: delay;
var int: total_delay = sum(delay);
constraint forall(i in 1..n)(
delay[i] = max(0, start[i] + duration[i] - due_date[i])
);
constraint cumulative(start, duration, resources, 1);
solve minimize total_delay;
MiniZinc: TSP (Minimizar Distancia Total)
Modelo clásico del Problema del Viajante (TSP) que busca minimizar la distancia total recorrida en un circuito cerrado.
array[1..numCities] of var int: succ;
var int: total_distance = sum(i in 1..numCities)(distance[i, succ[i]]);
constraint circuit(succ);
constraint forall(i in 1..numCities)(distance[i, succ[i]] != -1);
solve minimize total_distance;
Algoritmo de Prim (MST)
Implementación del algoritmo de Prim para encontrar el Árbol de Expansión Mínimo (MST), comenzando desde un nodo inicial.
import minigraph as nx
def solve(graph, node):
prim = nx.Graph()
visited_nodes = set()
visited_edges = set()
visited_nodes.add(node)
# Pool inicial de aristas conectadas al nodo de partida
edges_pool = set([(e[0], e[1], e[2]["weight"]) for e in graph.edges(node, data=True)])
edges_list = list()
# Continuar hasta que todos los nodos estén en el MST
while len(graph.nodes()) != len(prim.nodes()):
# Encontrar la arista de menor peso que no ha sido visitada
try:
edge = min(
edges_pool - visited_edges,
key=lambda e : e[2]
)
except ValueError:
break
visited_edges.add((edge[0], edge[1], edge[2]))
u, v, weight = edge
# Determinar qué nodo es nuevo (el que se añade al MST)
new_node = None
if u in visited_nodes and v not in visited_nodes:
new_node = v
elif v in visited_nodes and u not in visited_nodes:
new_node = u
if new_node is not None:
visited_nodes.add(new_node)
# Añadir arista al MST
prim.add_edge(u, v, weight=weight)
edges_list.append(edge)
# Añadir nuevas aristas del nuevo nodo al pool
for x in [(e[0], e[1], e[2]["weight"]) for e in graph.edges(new_node, data=True)]:
edges_pool.add(x)
return edges_list
Codificación de Huffman
Implementación del algoritmo de Huffman para generar códigos de longitud variable basados en la frecuencia de los caracteres, minimizando la longitud total codificada.
import minigraph as nx
def solve(string):
def get_freqs(string):
freqs = dict()
for char in string:
freqs[char] = freqs.get(char, 0) + 1
return freqs
def build_ordered_dict(freqs, other=None):
if other is not None:
freqs.update(other)
# Ordenar por frecuencia descendente
return dict(sorted(freqs.items(), key=lambda char : char[1], reverse=True))
def huffman_code(graph: nx.DiGraph, freqs, root):
sol = dict()
def dfs(node, prefix):
# Si el nodo es una hoja (carácter original)
if node[0] in freqs.keys():
sol[node[0]] = prefix
return
i = 0
# Recorrer vecinos ordenados (por frecuencia)
for n in sorted(graph.neighbors(node), key=lambda k : k[1]):
dfs(n, f"{prefix}{i}")
i += 1
dfs(root, "")
return sol
def build_graph(freqs):
heap = dict(freqs)
graph = nx.DiGraph()
root_node = None
while len(heap) >= 2:
# Tomar los dos nodos de menor frecuencia
node1 = heap.popitem()
node2 = heap.popitem()
sum_freq = node1[1] + node2[1]
label = f"{node1[0]}{node2[0]}"
new_node = (label, sum_freq)
graph.add_node(new_node)
graph.add_edge(new_node, (f"{node1[0]}", node1[1]))
graph.add_edge(new_node, (f"{node2[0]}", node2[1]))
# Reinsertar el nuevo nodo y reordenar
heap = build_ordered_dict(heap, {label : sum_freq})
root_node = new_node
if len(heap) == 1:
root_node = list(heap.items())[0]
return graph, root_node
freqs = build_ordered_dict(get_freqs(string))
graph, root_node = build_graph(dict(freqs))
huffman = huffman_code(graph, freqs, root_node)
# Calcular la longitud total codificada
sol = sum([freqs[k] * len(huffman[k]) for k in freqs.keys()])
return sol
Programación Dinámica: Tabulación
Implementación de la técnica de tabulación (bottom-up) para resolver un problema de subsecuencia (posiblemente la Subsecuencia Creciente Más Larga – LIS) y reconstruir la solución.
def solve_tabulation(items):
table = [0] * len(items)
taken = []
def fill_table():
# aux almacena la longitud de la subsecuencia creciente que termina en items[i]
aux = [1] * len(items)
for i in range(len(items) - 2, -1, -1):
for j in range(i + 1, len(items)):
if items[i] < items[j]:
aux[i] = max(aux[i], aux[j] + 1)
max_length = max(aux)
# Reconstrucción de la subsecuencia
for i in range(len(items)):
if aux[i] == max_length:
table[i] = 1
max_length -= 1
if max_length == 0:
break
return
def fill_taken():
nonlocal taken
for i in range(len(items)):
if table[i] == 1:
taken.append(i + 1)
return
fill_table()
fill_taken()
return len(taken), taken
Resumen de Algoritmos y Modelos
Implementaciones en Python
- Iterador de Fuerza Bruta (Base N)
- Problema de la Mochila (Fuerza Bruta)
- Ordenamiento Topológico (Fuerza Bruta)
- Iterador con Dominios Concretos
- Componentes Conectados (BFS)
- Devolución del Cambio (DFS)
- Árbol de Expansión Mínimo (Kruskal)
- Coloreado de Grafos (RLF)
- Distancia Mínima (Memoization)
- Camino Mínimo (BFS)
- Función Partition (Quicksort)
- Programación Dinámica con Memoización (Fórmula)
- Algoritmo de Prim (MST)
- Codificación de Huffman
- Programación Dinámica: Tabulación
Modelos de Optimización en MiniZinc
- Asignación de Trasplantes de Riñón
- Dosis de Pesticidas (Restricciones)
- Planificación de Ayuda Humanitaria (Prioridades)
- Flujo Máximo en Tuberías (Presión)
- Flujo Máximo con Capacidad de Nodo
- Knapsack 0/1 (Variables Binarias)
- Ladrón de Casas (Restricciones Adyacentes)
- Planificación Temporal (Cumulative)
- Reparto de Caramelos (Notas)
- Sudoku Solver
- Knapsack 0/1 (Variables de Conjunto)
- Ubicación de Instalaciones
- TSP (Minimizar Arista Máxima)
- Palillos (Criptoaritmética)
- Scheduling de Aerolíneas
- Shop Scheduling (Minimizar Retraso)
- TSP (Minimizar Distancia Total)