Permutaciones (backtracking)
Generar todas las ordenaciones de unos elementos eligiendo uno de los libres en cada posición y deshaciendo la elección al volver. Hay n! y el backtracking las recorre sin repetir ninguna.
nivel intermedioTambién: generar permutaciones, anagramas, todas las ordenaciones, next permutation
Visualízalo paso a paso
Cambia los datos, dale a reproducir y sigue cada paso en el dibujo, en la línea de Java que se ejecuta y en sus variables.
Permutaciones
Escribe unas letras distintas: se eligen de una en una entre las libres, y al completar una ordenación se deshace la última elección para probar otra.
- libre
Paso 1
Para cada posición se prueba, por orden, cada letra que siga libre. Al llenar las 3 posiciones se apunta la permutación y se deshace la última elección para probar la siguiente: es backtracking.
1static void permutar(char[] letras, boolean[] usada, StringBuilder actual, List<String> res) {
2 if (actual.length() == letras.length) {
3 res.add(actual.toString());
4 return;
5 }
6 for (int i = 0; i < letras.length; i++) {
7 if (usada[i]) continue;
8 usada[i] = true;
9 actual.append(letras[i]);
10 permutar(letras, usada, actual, res);
11 actual.deleteCharAt(actual.length() - 1);
12 usada[i] = false;
13 }
14}Variables
- actual
- —
- encontradas
- 0
- últimas
- —
Atajos con el foco dentro del visualizador: ← → paso a paso, Espacio reproducir o pausar, Inicio/Fin ir al principio o al final.
La idea
Una permutación es una forma de ordenar unos elementos. Con A, B y C hay 6: ABC, ACB, BAC, BCA, CAB y CBA. En general hay n! (n factorial): n opciones para la primera posición, n − 1 para la segunda, y así hasta 1. Crece muy deprisa: 10 elementos ya dan 3.628.800.
Para generarlas todas se usa el esquema del backtracking: elegir, explorar y deshacer. Se lleva la permutación que se está construyendo y un array de «usadas». En cada nivel se prueba, por orden, cada elemento libre: se marca, se añade y se llama a la función para la posición siguiente; al volver, se quita y se desmarca, para que el siguiente candidato encuentre todo como estaba. Cuando la permutación está completa, se apunta.
Si hay elementos repetidos (las letras de «AAB»), este esquema produciría permutaciones repetidas. La solución clásica es ordenar los elementos y, en cada posición, no elegir un elemento igual al anterior si el anterior está libre: esa letra ya se probó en esa posición. Así salen 3 permutaciones de «AAB» en vez de 6.
Hay otra forma, sin recursividad: el algoritmo de la siguiente permutación. A partir de una ordenación, calcula la que va justo después en orden alfabético con tres pasos sencillos, en O(n). Empezando por la ordenación de menor a mayor y repitiendo hasta que no haya siguiente, salen todas en orden. Es lo que hace std::next_permutation en C++.
Cuándo usarlo
- Generar todas las ordenaciones: anagramas, rutas que visitan unas ciudades en distinto orden, turnos.
- Búsquedas exhaustivas pequeñas: probar todas las asignaciones posibles (criptoaritmética, puzles) cuando n es pequeño.
- Como esqueleto de backtracking con poda: se descartan las ramas que ya no pueden dar una solución válida.
- Para pruebas: comprobar que un algoritmo funciona con cualquier orden de la entrada.
Cuándo no
- Con muchos elementos: 15 elementos son más de un billón de permutaciones. Hay que podar mucho o cambiar de idea (programación dinámica, voraces, heurísticas).
- Si solo hacen falta algunas al azar: se mezcla el array con Fisher-Yates en O(n).
- Si el orden no importa (elegir 3 de 10): eso son combinaciones, no permutaciones, y hay muchas menos.
Paso a paso
- Caso base. Si la permutación en construcción tiene todos los elementos, se apunta y se vuelve.
- Elegir. Para cada elemento todavía libre: se marca como usado y se añade al final.
- Explorar. Se llama a la función para rellenar la siguiente posición.
- Deshacer. Al volver, se quita el elemento y se desmarca, para probar el siguiente candidato en esta posición.
El código
Permutaciones sin repetidas
El backtracking con la regla para letras repetidas: «AAB» da 3 y no 6. Con «ROMA» salen 24 (y entre ellas, AMOR).
1import java.util.*;
2
3public class Main {
4 /** Backtracking que no repite permutaciones aunque haya letras repetidas: las letras van ordenadas y una
5 letra igual a la anterior solo se elige si la anterior ya está usada en esta rama. */
6 static void permutar(char[] letras, boolean[] usada, StringBuilder actual, List<String> res) {
7 if (actual.length() == letras.length) {
8 res.add(actual.toString());
9 return;
10 }
11 for (int i = 0; i < letras.length; i++) {
12 if (usada[i]) continue;
13 if (i > 0 && letras[i] == letras[i - 1] && !usada[i - 1]) continue; // esa letra ya se probó aquí
14 usada[i] = true;
15 actual.append(letras[i]);
16 permutar(letras, usada, actual, res);
17 actual.deleteCharAt(actual.length() - 1);
18 usada[i] = false;
19 }
20 }
21
22 static List<String> permutaciones(String s) {
23 char[] letras = s.toCharArray();
24 Arrays.sort(letras);
25 List<String> res = new ArrayList<>();
26 permutar(letras, new boolean[letras.length], new StringBuilder(), res);
27 return res;
28 }
29
30 public static void main(String[] args) {
31 for (String s : List.of("ABC", "AAB", "ROMA")) {
32 List<String> p = permutaciones(s);
33 String lista = p.size() <= 6 ? String.join(" ", p) : String.join(" ", p.subList(0, 6)) + " …";
34 System.out.println(s + ": " + p.size() + " → " + lista);
35 }
36 }
37}def permutar(letras, usada, actual, res):
"""Backtracking que no repite permutaciones aunque haya letras repetidas: las letras van ordenadas y una
letra igual a la anterior solo se elige si la anterior ya está usada en esta rama."""
if len(actual) == len(letras):
res.append("".join(actual))
return
for i in range(len(letras)):
if usada[i]:
continue
if i > 0 and letras[i] == letras[i - 1] and not usada[i - 1]: # esa letra ya se probó aquí
continue
usada[i] = True
actual.append(letras[i])
permutar(letras, usada, actual, res)
actual.pop()
usada[i] = False
def permutaciones(s):
letras = sorted(s)
res = []
permutar(letras, [False] * len(letras), [], res)
return res
for s in ["ABC", "AAB", "ROMA"]:
p = permutaciones(s)
lista = " ".join(p) if len(p) <= 6 else " ".join(p[:6]) + " …"
print(f"{s}: {len(p)} → {lista}")/** Backtracking que no repite permutaciones aunque haya letras repetidas: las letras van ordenadas y una
letra igual a la anterior solo se elige si la anterior ya está usada en esta rama. */
function permutar(letras, usada, actual, res) {
if (actual.length === letras.length) {
res.push(actual.join(""));
return;
}
for (let i = 0; i < letras.length; i++) {
if (usada[i]) continue;
if (i > 0 && letras[i] === letras[i - 1] && !usada[i - 1]) continue; // esa letra ya se probó aquí
usada[i] = true;
actual.push(letras[i]);
permutar(letras, usada, actual, res);
actual.pop();
usada[i] = false;
}
}
function permutaciones(s) {
const letras = [...s].sort();
const res = [];
permutar(letras, new Array(letras.length).fill(false), [], res);
return res;
}
for (const s of ["ABC", "AAB", "ROMA"]) {
const p = permutaciones(s);
const lista = p.length <= 6 ? p.join(" ") : p.slice(0, 6).join(" ") + " …";
console.log(`${s}: ${p.length} → ${lista}`);
}using System;
using System.Collections.Generic;
using System.Text;
class Program {
/// Backtracking que no repite permutaciones aunque haya letras repetidas: las letras van ordenadas y una
/// letra igual a la anterior solo se elige si la anterior ya está usada en esta rama.
static void Permutar(char[] letras, bool[] usada, StringBuilder actual, List<string> res) {
if (actual.Length == letras.Length) {
res.Add(actual.ToString());
return;
}
for (int i = 0; i < letras.Length; i++) {
if (usada[i]) continue;
if (i > 0 && letras[i] == letras[i - 1] && !usada[i - 1]) continue; // esa letra ya se probó aquí
usada[i] = true;
actual.Append(letras[i]);
Permutar(letras, usada, actual, res);
actual.Length--;
usada[i] = false;
}
}
static List<string> Permutaciones(string s) {
char[] letras = s.ToCharArray();
Array.Sort(letras);
var res = new List<string>();
Permutar(letras, new bool[letras.Length], new StringBuilder(), res);
return res;
}
static void Main() {
foreach (string s in new[] { "ABC", "AAB", "ROMA" }) {
var p = Permutaciones(s);
string lista = p.Count <= 6 ? string.Join(" ", p) : string.Join(" ", p.GetRange(0, 6)) + " …";
Console.WriteLine(quot;{s}: {p.Count} → {lista}");
}
}
}<?php
/** Backtracking que no repite permutaciones aunque haya letras repetidas: las letras van ordenadas y una
letra igual a la anterior solo se elige si la anterior ya está usada en esta rama. */
function permutar(array $letras, array &$usada, string $actual, array &$res): void {
if (strlen($actual) == count($letras)) {
$res[] = $actual;
return;
}
for ($i = 0; $i < count($letras); $i++) {
if ($usada[$i]) continue;
if ($i > 0 && $letras[$i] === $letras[$i - 1] && !$usada[$i - 1]) continue; // esa letra ya se probó aquí
$usada[$i] = true;
permutar($letras, $usada, $actual . $letras[$i], $res);
$usada[$i] = false;
}
}
function permutaciones(string $s): array {
$letras = str_split($s);
sort($letras);
$usada = array_fill(0, count($letras), false);
$res = [];
permutar($letras, $usada, "", $res);
return $res;
}
foreach (["ABC", "AAB", "ROMA"] as $s) {
$p = permutaciones($s);
$lista = count($p) <= 6 ? implode(" ", $p) : implode(" ", array_slice($p, 0, 6)) . " …";
echo "$s: " . count($p) . " → $lista\n";
}Salida al ejecutarlo (la misma en los 5 lenguajes)
ABC: 6 → ABC ACB BAC BCA CAB CBA AAB: 3 → AAB ABA BAA ROMA: 24 → AMOR AMRO AOMR AORM ARMO AROM …
La siguiente permutación, sin recursividad
Tres pasos convierten una ordenación en la siguiente en orden alfabético. Repitiéndolos desde 1 2 3 salen las 6.
1import java.util.*;
2
3public class Main {
4 /** Convierte a en la siguiente permutación en orden lexicográfico; false si ya era la última. */
5 static boolean siguiente(int[] a) {
6 int i = a.length - 2;
7 while (i >= 0 && a[i] >= a[i + 1]) i--; // 1. el último que tiene a su derecha uno mayor
8 if (i < 0) return false;
9 int j = a.length - 1;
10 while (a[j] <= a[i]) j--; // 2. el menor de su derecha que le supera
11 int t = a[i];
12 a[i] = a[j];
13 a[j] = t;
14 for (int x = i + 1, y = a.length - 1; x < y; x++, y--) { // 3. su derecha, de menor a mayor
15 t = a[x];
16 a[x] = a[y];
17 a[y] = t;
18 }
19 return true;
20 }
21
22 static String texto(int[] a, String sep) {
23 StringJoiner sj = new StringJoiner(sep);
24 for (int x : a) sj.add(String.valueOf(x));
25 return sj.toString();
26 }
27
28 public static void main(String[] args) {
29 int[] a = {1, 2, 3};
30 StringJoiner todas = new StringJoiner(" ");
31 do todas.add(texto(a, "")); while (siguiente(a));
32 System.out.println("Todas, en orden: " + todas);
33 int[] b = {1, 3, 5, 4, 2};
34 String antes = texto(b, " ");
35 siguiente(b);
36 System.out.println("Después de " + antes + " va " + texto(b, " "));
37 int[] c = {3, 2, 1};
38 System.out.println("¿Hay alguna después de 3 2 1? " + (siguiente(c) ? "sí" : "no: es la última"));
39 }
40}def siguiente(a):
"""Convierte a en la siguiente permutación en orden lexicográfico; False si ya era la última."""
i = len(a) - 2
while i >= 0 and a[i] >= a[i + 1]: # 1. el último que tiene a su derecha uno mayor
i -= 1
if i < 0:
return False
j = len(a) - 1
while a[j] <= a[i]: # 2. el menor de su derecha que le supera
j -= 1
a[i], a[j] = a[j], a[i]
a[i + 1:] = reversed(a[i + 1:]) # 3. su derecha, de menor a mayor
return True
def texto(a, sep):
return sep.join(str(x) for x in a)
a = [1, 2, 3]
todas = [texto(a, "")]
while siguiente(a):
todas.append(texto(a, ""))
print("Todas, en orden: " + " ".join(todas))
b = [1, 3, 5, 4, 2]
antes = texto(b, " ")
siguiente(b)
print(f"Después de {antes} va {texto(b, ' ')}")
c = [3, 2, 1]
print("¿Hay alguna después de 3 2 1? " + ("sí" if siguiente(c) else "no: es la última"))/** Convierte a en la siguiente permutación en orden lexicográfico; false si ya era la última. */
function siguiente(a) {
let i = a.length - 2;
while (i >= 0 && a[i] >= a[i + 1]) i--; // 1. el último que tiene a su derecha uno mayor
if (i < 0) return false;
let j = a.length - 1;
while (a[j] <= a[i]) j--; // 2. el menor de su derecha que le supera
[a[i], a[j]] = [a[j], a[i]];
for (let x = i + 1, y = a.length - 1; x < y; x++, y--) // 3. su derecha, de menor a mayor
[a[x], a[y]] = [a[y], a[x]];
return true;
}
const texto = (a, sep) => a.join(sep);
const a = [1, 2, 3];
const todas = [];
do todas.push(texto(a, "")); while (siguiente(a));
console.log("Todas, en orden: " + todas.join(" "));
const b = [1, 3, 5, 4, 2];
const antes = texto(b, " ");
siguiente(b);
console.log(`Después de ${antes} va ${texto(b, " ")}`);
const c = [3, 2, 1];
console.log("¿Hay alguna después de 3 2 1? " + (siguiente(c) ? "sí" : "no: es la última"));using System;
using System.Collections.Generic;
class Program {
/// Convierte a en la siguiente permutación en orden lexicográfico; false si ya era la última.
static bool Siguiente(int[] a) {
int i = a.Length - 2;
while (i >= 0 && a[i] >= a[i + 1]) i--; // 1. el último que tiene a su derecha uno mayor
if (i < 0) return false;
int j = a.Length - 1;
while (a[j] <= a[i]) j--; // 2. el menor de su derecha que le supera
(a[i], a[j]) = (a[j], a[i]);
Array.Reverse(a, i + 1, a.Length - i - 1); // 3. su derecha, de menor a mayor
return true;
}
static string Texto(int[] a, string sep) => string.Join(sep, a);
static void Main() {
int[] a = { 1, 2, 3 };
var todas = new List<string>();
do todas.Add(Texto(a, "")); while (Siguiente(a));
Console.WriteLine("Todas, en orden: " + string.Join(" ", todas));
int[] b = { 1, 3, 5, 4, 2 };
string antes = Texto(b, " ");
Siguiente(b);
Console.WriteLine(quot;Después de {antes} va {Texto(b, " ")}");
int[] c = { 3, 2, 1 };
Console.WriteLine("¿Hay alguna después de 3 2 1? " + (Siguiente(c) ? "sí" : "no: es la última"));
}
}<?php
/** Convierte $a en la siguiente permutación en orden lexicográfico; false si ya era la última. */
function siguiente(array &$a): bool {
$i = count($a) - 2;
while ($i >= 0 && $a[$i] >= $a[$i + 1]) $i--; // 1. el último que tiene a su derecha uno mayor
if ($i < 0) return false;
$j = count($a) - 1;
while ($a[$j] <= $a[$i]) $j--; // 2. el menor de su derecha que le supera
[$a[$i], $a[$j]] = [$a[$j], $a[$i]];
for ($x = $i + 1, $y = count($a) - 1; $x < $y; $x++, $y--) // 3. su derecha, de menor a mayor
[$a[$x], $a[$y]] = [$a[$y], $a[$x]];
return true;
}
function texto(array $a, string $sep): string {
return implode($sep, $a);
}
$a = [1, 2, 3];
$todas = [];
do $todas[] = texto($a, ""); while (siguiente($a));
echo "Todas, en orden: " . implode(" ", $todas) . "\n";
$b = [1, 3, 5, 4, 2];
$antes = texto($b, " ");
siguiente($b);
echo "Después de $antes va " . texto($b, " ") . "\n";
$c = [3, 2, 1];
echo "¿Hay alguna después de 3 2 1? " . (siguiente($c) ? "sí" : "no: es la última") . "\n";Salida al ejecutarlo (la misma en los 5 lenguajes)
Todas, en orden: 123 132 213 231 312 321 Después de 1 3 5 4 2 va 1 4 2 3 5 ¿Hay alguna después de 3 2 1? no: es la última
Traza: El backtracking con A, B y C
| Construida | Libres | Qué pasa |
|---|---|---|
| A | B C | elige la A |
| AB | C | elige la B |
| ABC | — | completa: se apunta |
| AC | B | elige la C |
| ACB | — | completa: se apunta |
| B | A C | elige la B |
| BA | C | elige la A |
| BAC | — | completa: se apunta |
| BC | A | elige la C |
| BCA | — | completa: se apunta |
| C | A B | elige la C |
| CA | B | elige la A |
| CAB | — | completa: se apunta |
| CB | A | elige la B |
| CBA | — | completa: se apunta |
Tras cada permutación completa se deshacen elecciones hasta el último nivel que aún tiene letras sin probar.
Complejidad
| Operación | Coste |
|---|---|
| Generar todas | O(n · n!): n! permutaciones de n elementos |
| Con repetidos (a letras iguales, b iguales…) | n! / (a! · b! · …) permutaciones distintas |
| Siguiente permutación | O(n) |
| Mezclar al azar (Fisher-Yates) | O(n) |
Ningún algoritmo que escriba todas las permutaciones puede bajar de n!: es lo que ocupa la salida.
- Mejor caso: O(n!)
- Caso medio: O(n!)
- Peor caso: O(n!)
Hay n! permutaciones y cada una cuesta n escribirla: O(n · n!). Ningún algoritmo que las liste todas puede bajar de ahí. Las curvas grises son las demás clases, para comparar.
En la práctica
- Pruebas de software: ejecutar un caso con todos los órdenes posibles de unas operaciones para buscar fallos de concurrencia.
- Problemas de rutas pequeñas (el viajante con pocas ciudades) se resuelven probando todas las ordenaciones.
- Los juegos de palabras y los generadores de anagramas.
- Criptografía clásica: las cifras de transposición reordenan las letras según una permutación.
- En C++,
std::next_permutation; en Python,itertools.permutations.
Errores típicos
- Olvidar deshacer la elección al volver (quitar el elemento o desmarcarlo): las ramas siguientes ven un estado corrupto.
- Guardar la permutación en curso por referencia en la lista de resultados: al final todas apuntan al mismo objeto vacío. Hay que guardar una copia (
toString(),new ArrayList<>(actual)). - No tratar los repetidos y obtener permutaciones duplicadas.
- Intentar generar todas con n grande: con 13 elementos son más de 6.000 millones.
- Confundir permutaciones (importa el orden) con combinaciones (no importa).
Ejercicios
Cada ejercicio se corrige solo con sus pruebas (algunas ocultas). Escribe tu solución en el editor y pulsa Ejecutar o Comprobar; la solución explicada está debajo, por si te atascas.
1. Anagramas
Para cada palabra, escribe cuántos anagramas distintos tiene (ordenaciones de sus letras, incluida ella misma) y los primeros en orden alfabético. El main ya escribe el resultado: completa anagramas para que devuelva todos, sin repetidos y ordenados.
- Entrada: una palabra por línea, de 1 a 7 letras de la a a la z (se pasan a minúsculas).
- Salida:
ola: 6 anagramas: alo aol lao loa oal ola. Si hay más de 12, los 12 primeros,…y el último. - Otra cosa:
Palabra no válida: «…» (de 1 a 7 letras de la a a la z).
Ejemplo
ola sol
ola: 6 anagramas: alo aol lao loa oal ola sol: 6 anagramas: los lso ols osl slo sol
Ver la solución explicada
1import java.util.*;
2
3public class Main {
4 /** Todas las ordenaciones distintas de las letras de la palabra, de menor a mayor. */
5 static List<String> anagramas(String palabra) {
6 char[] letras = palabra.toCharArray();
7 Arrays.sort(letras);
8 List<String> res = new ArrayList<>();
9 generar(letras, new boolean[letras.length], new StringBuilder(), res);
10 return res;
11 }
12
13 static void generar(char[] letras, boolean[] usada, StringBuilder actual, List<String> res) {
14 if (actual.length() == letras.length) {
15 res.add(actual.toString());
16 return;
17 }
18 for (int i = 0; i < letras.length; i++) {
19 if (usada[i] || (i > 0 && letras[i] == letras[i - 1] && !usada[i - 1])) continue;
20 usada[i] = true;
21 actual.append(letras[i]);
22 generar(letras, usada, actual, res);
23 actual.setLength(actual.length() - 1);
24 usada[i] = false;
25 }
26 }
27
28 public static void main(String[] args) {
29 Scanner sc = new Scanner(System.in);
30 while (sc.hasNextLine()) {
31 String palabra = sc.nextLine().trim().toLowerCase();
32 if (palabra.isEmpty()) continue;
33 if (!palabra.matches("[a-z]{1,7}")) {
34 System.out.println("Palabra no válida: «" + palabra + "» (de 1 a 7 letras de la a a la z)");
35 continue;
36 }
37 List<String> a = anagramas(palabra);
38 String lista = a.size() <= 12 ? String.join(" ", a) : String.join(" ", a.subList(0, 12)) + " … " + a.get(a.size() - 1);
39 System.out.println(palabra + ": " + a.size() + (a.size() == 1 ? " anagrama: " : " anagramas: ") + lista);
40 }
41 }
42}«Mississippi» tendría 11! = 39.916.800 ordenaciones, pero solo 34.650 distintas: evitar los repetidos al generar ahorra muchísimo trabajo frente a generar todo y filtrar.
Como las letras empiezan ordenadas y se prueban en orden, las permutaciones salen ya ordenadas sin ordenar nada al final.
2. Sentar a los invitados
Unos invitados se sientan en una fila de butacas, pero algunas parejas no se pueden sentar juntas. Cuenta de cuántas formas se pueden colocar y escribe la primera en orden alfabético. Con 9 invitados hay 362.880 ordenaciones, así que hay que podar: en cuanto un invitado queda al lado de un enemigo, esa rama se abandona. Completa sentar.
- Primera línea: los nombres de los invitados (de 2 a 9, distintos). Después, una pareja de enemigos por línea:
ana luis. - Salida:
Formas posibles: 12yPrimera en orden alfabético: ana, carlos, luis, marta, oImposible: no hay forma de sentarlos. - Pareja con un nombre desconocido o repetido:
Línea no válida: «…».
Ejemplo
ana luis marta carlos ana luis marta carlos
Formas posibles: 8 Primera en orden alfabético: ana, carlos, luis, marta
Ver la solución explicada
1import java.util.*;
2
3public class Main {
4 static String[] nombres; // en orden alfabético
5 static boolean[][] enemigos; // enemigos[i][j]: i y j no se pueden sentar juntos
6 static long formas = 0;
7 static String primera = null; // la primera ordenación válida que se encuentra
8
9 /** Prueba a sentar en la siguiente butaca a cada invitado libre que no sea enemigo del de su izquierda. */
10 static void sentar(List<Integer> actual, boolean[] sentado) {
11 if (actual.size() == nombres.length) {
12 formas++;
13 if (primera == null) {
14 StringJoiner sj = new StringJoiner(", ");
15 for (int i : actual) sj.add(nombres[i]);
16 primera = sj.toString();
17 }
18 return;
19 }
20 for (int i = 0; i < nombres.length; i++) {
21 if (sentado[i]) continue;
22 if (!actual.isEmpty() && enemigos[actual.get(actual.size() - 1)][i]) continue; // poda: no puede ir aquí
23 sentado[i] = true;
24 actual.add(i);
25 sentar(actual, sentado);
26 actual.remove(actual.size() - 1);
27 sentado[i] = false;
28 }
29 }
30
31 public static void main(String[] args) {
32 Scanner sc = new Scanner(System.in);
33 String cabecera = sc.hasNextLine() ? sc.nextLine().trim() : "";
34 TreeSet<String> invitados = new TreeSet<>(Arrays.asList(cabecera.split("\\s+")));
35 invitados.remove("");
36 if (invitados.size() < 2 || invitados.size() > 9) {
37 System.out.println("Hacen falta de 2 a 9 invitados distintos en la primera línea");
38 return;
39 }
40 nombres = invitados.toArray(new String[0]);
41 List<String> lista = Arrays.asList(nombres);
42 enemigos = new boolean[nombres.length][nombres.length];
43 while (sc.hasNextLine()) {
44 String linea = sc.nextLine().trim();
45 if (linea.isEmpty()) continue;
46 String[] p = linea.split("\\s+");
47 if (p.length != 2 || !lista.contains(p[0]) || !lista.contains(p[1]) || p[0].equals(p[1])) {
48 System.out.println("Línea no válida: «" + linea + "»");
49 continue;
50 }
51 int a = lista.indexOf(p[0]), b = lista.indexOf(p[1]);
52 enemigos[a][b] = enemigos[b][a] = true;
53 }
54 sentar(new ArrayList<>(), new boolean[nombres.length]);
55 if (formas == 0) System.out.println("Imposible: no hay forma de sentarlos");
56 else {
57 System.out.println("Formas posibles: " + formas);
58 System.out.println("Primera en orden alfabético: " + primera);
59 }
60 }
61}La poda es lo que convierte la fuerza bruta en backtracking: una rama con dos enemigos juntos se corta en cuanto aparece, sin generar las miles de filas que empezarían así.
Comprobar la fila al final también daría el resultado correcto, pero generando siempre las n! ordenaciones.
Test
Test: Permutaciones (backtracking)
0/5 respondidas · 0 aciertosElige una respuesta en cada pregunta: verás al momento si es correcta y por qué. Con un 80 % de aciertos se da por superada.
1.¿Cuántas permutaciones tiene un conjunto de 5 elementos distintos?
2.En el backtracking de permutaciones, ¿qué se hace al volver de la llamada recursiva?
3.¿Cuántas permutaciones distintas tiene «AAB»?
4.¿Qué permutación va justo después de 1 3 2 en orden lexicográfico?
5.¿Por qué se guarda una copia de la permutación al apuntarla?