Combinaciones y Permutaciones
Domina el conteo de combinaciones y permutaciones para resolver problemas de combinatoria
Principios básicos de conteo
Principio de la suma
Si hay formas de hacer algo Y formas de hacer otra cosa diferente, hay formas de hacer una u otra.
Principio del producto
Si hay formas de hacer algo Y LUEGO formas de hacer otra cosa, hay formas de hacer ambas cosas.
Permutaciones
Una permutación es un arreglo ordenado de elementos. El orden importa.
Permutaciones de elementos
Ejemplo: ¿De cuántas formas podemos ordenar las letras A, B, C?
ABC, ACB, BAC, BCA, CAB, CBA → 6 formas =
Permutaciones de elementos tomados de
Ejemplo: ¿Cuántos números de 3 dígitos distintos se pueden formar con 5?
const long long MOD = 1e9 + 7;
long long permutaciones(int n, int r, long long* fact, long long* invFact) {
if (r > n) return 0;
return fact[n] * invFact[n - r] % MOD;
}
Permutaciones con repetición
Si tenemos elementos donde hay elementos del tipo 1, del tipo 2, etc.:
Ejemplo: ¿De cuántas formas podemos ordenar las letras de "MISSISSIPPI"?
- M: 1, I: 4, S: 4, P: 2
- Total:
long long permutacionesConRepeticion(vector<int>& frecuencias, long long* fact, long long* invFact) {
int n = 0;
for (int f : frecuencias) n += f;
long long resultado = fact[n];
for (int f : frecuencias) {
resultado = resultado * invFact[f] % MOD;
}
return resultado;
}
Combinaciones
Una combinación es una selección de elementos donde el orden NO importa.
Coeficiente binomial
Ejemplo: ¿De cuántas formas podemos elegir 3 personas de un grupo de 5?
long long combinaciones(int n, int r, long long* fact, long long* invFact) {
if (r > n || r < 0) return 0;
return fact[n] * invFact[r] % MOD * invFact[n - r] % MOD;
}
Propiedades importantes
- (simetría)
- (Pascal)
- (si )
Triángulo de Pascal
El triángulo de Pascal visualiza los coeficientes binomiales:
1
1 1
1 2 1
1 3 3 1
1 4 6 4 1
1 5 10 10 5 1
Cada número es la suma de los dos números directamente arriba de él.
Implementación
const int MAXN = 5000;
long long pascal[MAXN + 1][MAXN + 1];
void construirPascal() {
for (int i = 0; i <= MAXN; i++) {
pascal[i][0] = 1;
for (int j = 1; j <= i; j++) {
pascal[i][j] = (pascal[i-1][j-1] + pascal[i-1][j]) % MOD;
}
}
}
Combinaciones con repetición
Elegir elementos de tipos, permitiendo repetición:
Ejemplo: ¿De cuántas formas podemos distribuir 5 dulces idénticos entre 3 niños?
Template completo
#include <bits/stdc++.h>
using namespace std;
const long long MOD = 1e9 + 7;
const int MAXN = 2e6;
long long fact[MAXN + 1];
long long invFact[MAXN + 1];
long long potencia(long long base, long long exp) {
long long resultado = 1;
base %= MOD;
while (exp > 0) {
if (exp & 1) resultado = resultado * base % MOD;
base = base * base % MOD;
exp >>= 1;
}
return resultado;
}
void precalcular() {
fact[0] = 1;
for (int i = 1; i <= MAXN; i++) {
fact[i] = fact[i-1] * i % MOD;
}
invFact[MAXN] = potencia(fact[MAXN], MOD - 2);
for (int i = MAXN - 1; i >= 0; i--) {
invFact[i] = invFact[i + 1] * (i + 1) % MOD;
}
}
// C(n, r) = n! / (r! * (n-r)!)
long long C(int n, int r) {
if (r > n || r < 0) return 0;
return fact[n] * invFact[r] % MOD * invFact[n - r] % MOD;
}
// P(n, r) = n! / (n-r)!
long long P(int n, int r) {
if (r > n) return 0;
return fact[n] * invFact[n - r] % MOD;
}
// Combinaciones con repetición: C(n + r - 1, r)
long long H(int n, int r) {
return C(n + r - 1, r);
}
int main() {
precalcular();
cout << "C(5, 3) = " << C(5, 3) << endl; // 10
cout << "P(5, 3) = " << P(5, 3) << endl; // 60
cout << "H(3, 5) = " << H(3, 5) << endl; // 21
return 0;
}
Problemas clásicos
Problema 1: Caminos en una cuadrícula
¿Cuántos caminos hay desde hasta moviéndose solo derecha o arriba?
Necesitamos movimientos a la derecha y hacia arriba, en total movimientos.
long long caminosCuadricula(int n, int m) {
return C(n + m, n);
}
Problema 2: Caminos con obstáculos
Usar programación dinámica con combinaciones:
#include <bits/stdc++.h>
using namespace std;
const long long MOD = 1e9 + 7;
// ... (precalcular factoriales)
int main() {
precalcular();
int n, m;
cin >> n >> m;
vector<string> grid(n);
for (int i = 0; i < n; i++) cin >> grid[i];
vector<vector<long long>> dp(n, vector<long long>(m, 0));
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
if (grid[i][j] == '#') continue;
if (i == 0 && j == 0) {
dp[i][j] = 1;
} else {
if (i > 0) dp[i][j] = (dp[i][j] + dp[i-1][j]) % MOD;
if (j > 0) dp[i][j] = (dp[i][j] + dp[i][j-1]) % MOD;
}
}
}
cout << dp[n-1][m-1] << endl;
return 0;
}
Problema 3: Subconjuntos de tamaño
¿Cuántos subconjuntos de tamaño exactamente tiene un conjunto de elementos?
Problema 4: Distribución de objetos
Distribuir objetos idénticos en cajas distinguibles:
- Sin restricciones: (Stars and Bars)
- Al menos 1 por caja:
// n objetos en k cajas
long long distribuir(int n, int k) {
return C(n + k - 1, k - 1);
}
// n objetos en k cajas, cada caja al menos 1
long long distribuirMinimo1(int n, int k) {
if (n < k) return 0;
return C(n - 1, k - 1);
}
Problema 5: Secuencias binarias
¿Cuántas secuencias binarias de longitud tienen exactamente unos?
Problema 6: Suma de combinaciones
Calcular :
long long sumaCombinaciones(int n, int k) {
long long suma = 0;
for (int i = 0; i <= k && i <= n; i++) {
suma = (suma + C(n, i)) % MOD;
}
return suma;
}
Problema 7: Derangements (permutaciones sin puntos fijos)
Un derangement es una permutación donde ningún elemento está en su posición original.
long long derangements(int n) {
if (n == 0) return 1;
if (n == 1) return 0;
long long dp[n + 1];
dp[0] = 1;
dp[1] = 0;
for (int i = 2; i <= n; i++) {
dp[i] = (i - 1) * ((dp[i-1] + dp[i-2]) % MOD) % MOD;
}
return dp[n];
}
Identidades útiles
Identidad de Vandermonde
Identidad del bastón de hockey
Números de Catalan
Aplicaciones: paréntesis balanceados, árboles binarios, caminos Dyck.
long long catalan(int n) {
return C(2 * n, n) * potencia(n + 1, MOD - 2) % MOD;
}
Resumen
| Fórmula | Descripción | Expresión |
|---|---|---|
| Permutaciones de | ||
| Permutaciones de de | ||
| Combinaciones | ||
| Combinaciones con repetición | ||
| Stars & Bars | objetos en cajas |
