0

Estructuras de Datos en C# 09: Programación Dinámica y Algoritmos de Backtracking

Introducción a la clase

En esta clase exploraremos dos técnicas avanzadas en la resolución de problemas: programación dinámica y backtracking. Estas estrategias se utilizan para resolver problemas complejos de manera eficiente, reduciendo el número de cálculos innecesarios o explorando múltiples soluciones de manera ordenada.

¿Qué obtendrás de esta clase?

  • Comprender qué es la programación dinámica y su aplicación en problemas de optimización.
  • Aprender sobre backtracking y cómo se usa para resolver problemas de búsqueda y combinaciones.
  • Implementar algoritmos de programación dinámica y backtracking en C#.
  • Aplicar estos algoritmos en casos reales.

Programación Dinámica

La programación dinámica (DP) es una técnica de optimización que almacena resultados intermedios para evitar cálculos repetitivos en problemas que tienen una subestructura óptima y superposición de subproblemas.

Ejemplo de Problema: Serie de Fibonacci

El cálculo de la Serie de Fibonacci es un caso clásico donde almacenar resultados intermedios reduce significativamente el número de cálculos.

Implementación Recursiva Sin Programación Dinámica (Ineficiente – O(2ⁿ))

using System;

class Program
{
    static int Fibonacci(int n)
    {
        if (n <= 1) return n;
        return Fibonacci(n - 1) + Fibonacci(n - 2);
    }

    static void Main()
    {
        Console.WriteLine("Ingrese un número para calcular Fibonacci:");
        int n = Convert.ToInt32(Console.ReadLine());

        Console.WriteLine($"Fibonacci({n}) = " + Fibonacci(n));
    }
}

Esta implementación es ineficiente, ya que recalcula valores repetidamente.

Implementación con Programación Dinámica (Eficiente – O(n))

using System;
using System.Collections.Generic;

class Program
{
    static Dictionary<int, long> memo = new Dictionary<int, long>();

    static long FibonacciDP(int n)
    {
        if (n <= 1) return n;
        if (!memo.ContainsKey(n))
            memo[n] = FibonacciDP(n - 1) + FibonacciDP(n - 2);

        return memo[n];
    }

    static void Main()
    {
        Console.WriteLine("Ingrese un número para calcular Fibonacci:");
        int n = Convert.ToInt32(Console.ReadLine());

        Console.WriteLine($"Fibonacci({n}) = " + FibonacciDP(n));
    }
}

Aquí, almacenamos los resultados previos en un diccionario, reduciendo los cálculos innecesarios.

Backtracking

El backtracking es una técnica utilizada para resolver problemas de búsqueda y combinaciones explorando todas las soluciones posibles y retrocediendo cuando una opción no es válida.

Ejemplo de Problema: Sudoku o el Problema de la Mochila

Un ejemplo clásico es el Problema de la N-Queens, donde debemos colocar N reinas en un tablero de ajedrez sin que se ataquen entre sí.

Implementación del Problema de la N-Queens en C#

using System;

class NQueens
{
    static int N = 8; 
    static int[,] tablero = new int[N, N];

    static bool EsSeguro(int fila, int col)
    {
        for (int i = 0; i < col; i++)
            if (tablero[fila, i] == 1) return false;

        for (int i = fila, j = col; i >= 0 && j >= 0; i--, j--)
            if (tablero[i, j] == 1) return false;

        for (int i = fila, j = col; i < N && j >= 0; i++, j--)
            if (tablero[i, j] == 1) return false;

        return true;
    }

    static bool ResolverNQueens(int col)
    {
        if (col >= N) return true;

        for (int i = 0; i < N; i++)
        {
            if (EsSeguro(i, col))
            {
                tablero[i, col] = 1;

                if (ResolverNQueens(col + 1)) return true;

                tablero[i, col] = 0;
            }
        }
        return false;
    }

    static void ImprimirTablero()
    {
        for (int i = 0; i < N; i++)
        {
            for (int j = 0; j < N; j++)
                Console.Write(tablero[i, j] + " ");
            Console.WriteLine();
        }
    }

    static void Main()
    {
        if (ResolverNQueens(0))
            ImprimirTablero();
        else
            Console.WriteLine("No hay solución");
    }
}

Este código utiliza backtracking para encontrar una disposición válida de las N reinas en un tablero de ajedrez.

Comparación entre Programación Dinámica y Backtracking

CaracterísticaProgramación DinámicaBacktracking
Uso principalOptimización de problemas con subestructura óptimaBúsqueda de soluciones mediante exploración
EnfoqueAlmacenar resultados previos para evitar cálculos repetidosExplorar todas las soluciones posibles y retroceder cuando sea necesario
ComplejidadGeneralmente O(n) o O(n²)Puede ser O(2ⁿ) o peor
Aplicaciones comunesFibonacci, Problema de la Mochila, Caminos en matricesSudoku, N-Queens, Generación de combinaciones

Caso de uso real

Un sistema de recomendación de rutas puede usar programación dinámica para encontrar el camino más corto entre dos puntos almacenando caminos previos.
Un algoritmo de inteligencia artificial en juegos usa backtracking para evaluar todas las posibles jugadas antes de tomar una decisión.

Ejemplo en C#: Ruta más corta usando Programación Dinámica

using System;

class Program
{
    static int Minimo(int a, int b, int c) => Math.Min(a, Math.Min(b, c));

    static int CaminoMasCorto(int[,] grid, int filas, int cols)
    {
        int[,] dp = new int[filas, cols];

        dp[0, 0] = grid[0, 0];

        for (int i = 1; i < filas; i++)
            dp[i, 0] = dp[i - 1, 0] + grid[i, 0];

        for (int j = 1; j < cols; j++)
            dp[0, j] = dp[0, j - 1] + grid[0, j];

        for (int i = 1; i < filas; i++)
            for (int j = 1; j < cols; j++)
                dp[i, j] = grid[i, j] + Minimo(dp[i - 1, j], dp[i, j - 1], dp[i - 1, j - 1]);

        return dp[filas - 1, cols - 1];
    }

    static void Main()
    {
        int[,] grid = {
            { 1, 3, 1 },
            { 1, 5, 1 },
            { 4, 2, 1 }
        };

        Console.WriteLine("El camino más corto tiene costo: " + CaminoMasCorto(grid, 3, 3));
    }
}

Este algoritmo encuentra el camino más corto en una cuadrícula usando programación dinámica.

Próxima clase: Aplicaciones de estructuras de datos en la vida real

En la siguiente clase exploraremos casos reales donde se aplican las estructuras de datos, incluyendo bases de datos, redes sociales, sistemas de recomendación y más.

Fernando Sonego

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *