Жадные Алгоритмы В C#

Скачать

📘 Жадные алгоритмы в C#

Жадные (greedy) алгоритмы — это стратегия, при которой на каждом шаге выбирается локально оптимальное решение с надеждой на получение глобального оптимума. Они не всегда дают наилучший результат, но часто работают эффективно для большого класса задач.


🔍 Основная идея

Жадный алгоритм строит решение шаг за шагом, принимая на каждом этапе наилучшее с точки зрения текущего состояния решение, не оглядываясь назад и не анализируя последствия.


✅ Когда работают

  • Когда задача обладает свойством жадности (greedy-choice property) — локальный выбор приводит к глобально оптимальному решению.
  • Когда задача обладает оптимальной подструктурой — оптимальное решение задачи включает в себя оптимальные решения её подзадач.

📦 Пример 1: Задача о сдаче (монетах)

Условие: Дано количество денег и список монет. Найти минимальное количество монет для выдачи суммы.

⚠️ Работает корректно только при жадной системе монет (например, 1, 5, 10, 25).

int[] coins = { 25, 10, 5, 1 }; // отсортированы по убыванию
int amount = 63;
var result = new List<int>();

foreach (int coin in coins)
{
    while (amount >= coin)
    {
        amount -= coin;
        result.Add(coin);
    }
}

Console.WriteLine("Монеты: " + string.Join(", ", result));

📦 Пример 2: Активности с максимальным количеством (Activity Selection)

Условие: Дано N мероприятий с временем начала и окончания. Нужно выбрать максимальное число непересекающихся.

Жадная стратегия: Всегда выбирать мероприятие, которое заканчивается раньше всех.

class Activity
{
    public int Start { get; set; }
    public int End { get; set; }
}

var activities = new List<Activity>
{
    new() { Start = 1, End = 3 },
    new() { Start = 2, End = 5 },
    new() { Start = 4, End = 6 },
    new() { Start = 6, End = 7 },
    new() { Start = 5, End = 9 },
    new() { Start = 8, End = 9 }
};

var sorted = activities.OrderBy(a => a.End).ToList();
var result = new List<Activity>();

int lastEnd = 0;
foreach (var act in sorted)
{
    if (act.Start >= lastEnd)
    {
        result.Add(act);
        lastEnd = act.End;
    }
}

foreach (var a in result)
    Console.WriteLine($"({a.Start}, {a.End})");

📦 Пример 3: Задача о покрытии отрезков (Interval Covering)

Условие: Покрыть отрезок [0, M] минимальным числом отрезков из заданного набора.

Жадный подход: Всегда выбираем отрезок, который начинается до текущей позиции и имеет максимальное правое окончание.

class Segment
{
    public int Start { get; set; }
    public int End { get; set; }
}

int M = 10;
var segments = new List<Segment>
{
    new() { Start = 0, End = 5 },
    new() { Start = 3, End = 7 },
    new() { Start = 6, End = 10 },
    new() { Start = 8, End = 11 }
};

segments = segments.OrderBy(s => s.Start).ToList();
var result = new List<Segment>();
int current = 0, i = 0;

while (current < M)
{
    int maxEnd = -1;
    Segment chosen = null;

    while (i < segments.Count && segments[i].Start <= current)
    {
        if (segments[i].End > maxEnd)
        {
            maxEnd = segments[i].End;
            chosen = segments[i];
        }
        i++;
    }

    if (chosen == null)
    {
        Console.WriteLine("Невозможно покрыть отрезок");
        break;
    }

    result.Add(chosen);
    current = chosen.End;
}

foreach (var s in result)
    Console.WriteLine($"[{s.Start}, {s.End}]");

📦 Пример 4: Минимум платформ на вокзале (Train Platforms)

Условие: Дано время прибытия и отбытия поездов. Определить минимальное количество платформ, чтобы избежать пересечений.

int[] arrival = { 900, 940, 950, 1100, 1500, 1800 };
int[] departure = { 910, 1200, 1120, 1130, 1900, 2000 };

Array.Sort(arrival);
Array.Sort(departure);

int i = 0, j = 0;
int platforms = 0, maxPlatforms = 0;

while (i < arrival.Length && j < departure.Length)
{
    if (arrival[i] <= departure[j])
    {
        platforms++;
        i++;
        maxPlatforms = Math.Max(maxPlatforms, platforms);
    }
    else
    {
        platforms--;
        j++;
    }
}

Console.WriteLine($"Минимум платформ: {maxPlatforms}");

❗ Пример, где жадность не работает: размен монет

Если монеты: [1, 3, 4], сумма = 6

Жадный выберет: 4 + 1 + 1 = 3 монеты, но оптимально: 3 + 3 = 2 монеты.

➡ Используйте динамическое программирование, если:

  • есть пересекающиеся подзадачи;
  • жадный выбор может привести к неоптимальному результату.

🧠 Заключение

Жадные алгоритмы:

  • Просты и быстры (обычно O(n log n) из-за сортировки).
  • Отличны для задач с локальным выбором (задачи покрытия, задачи тайминга).
  • Но не универсальны — важно проверять применимость.