Розбиття на k підмасивів#9020

Розбиття на k підмасивів (Розбір)

Будемо шукати оптимальне значення за допомогою бінарного пошуку. Очевидно, що воно не може бути менше (оскільки числа в масиві натуральні) і не може перевищувати суму всіх елементів масиву. Встановимо межі: left = 1, right = сума всіх чисел масиву. Тоді відповідь гарантовано знаходиться в діапазоні [left; right].

Розглянемо допоміжну підзадачу: чи можна розбити масив на підмасивів так, щоб максимальна сума серед усіх підмасивів не перевищувала заданого значення s_0. Якщо в масиві є хоча б один елемент, що перевищує s_0, таке розбиття неможливе. В інших випадках будемо послідовно формувати підмасиви з сумою елементів, що не перевищує s_0. Якщо в результаті отримаємо не більше підмасивів, то таке розбиття можливе.

Приклад

У другому прикладі розглянемо можливі розбиття масиву на підмасиви при різних значеннях s_0:

Реалізація алгоритму

Оголосимо константу MAX = 10^6 і робочий масив .

#define MAX 1000000
int m[MAX];

Функція f повертає , якщо масив можна розбити на підмасивів так, щоб максимальна сума серед усіх підмасивів не перевищувала заданого значення s_0.

int f(long long s0)
{
  long long sum = 0;
  int cnt = 1;
  for (int i = 0; i < n; i++)
  {

Якщо якийсь елемент масиву більше s_0, то таке розбиття неможливе.

    if (m[i] > s0) return 0;

Обчислюємо суму поточного підмасиву.

    sum += m[i];

Як тільки поточна сума перевищує значення s_0, збільшуємо лічильник підмасивів на і починаємо накопичувати суму наступного підмасиву.

    if (sum > s0)
    {
      sum = m[i];
      cnt++;
    }
  }

Якщо масив можна розбити не більше ніж на підмасивів, значить, його можна розбити і точно на k~(k \le n) підмасивів.

  if (cnt <= k) return 1;
  return 0;
}

Основна частина програми. Читаємо вхідні дані. У змінній обчислюємо суму всіх елементів масиву.

scanf("%d %d", &n, &k);
left = 1; right = 0;
for (i = 0; i < n; i++)
{
  scanf("%d", &m[i]);
  right += m[i];
}

Шукане мінімальне значення максимальної суми підмасивів знаходиться в діапазоні [left; right]. Запускаємо бінарний пошук на цьому відрізку.

while (left < right)
{
  mid = (left + right) / 2;
  if (f(mid) == 1)
    right = mid;
  else
    left = mid + 1;
}

Виводимо відповідь.

printf("%lld\n", left);