Мінімум збільшень#9023

Мінімум збільшень (Розбір)

Нехай мінімальна кількість операцій перетворює початковий масив

m[0], m[1], ..., m[n - 1]

в арифметичну прогресію виду

x, x + 1, x + 2, ..., x + n - 1

Очевидно, що m[0] \le x \le max(m[0], m[1], ..., m[n - 1]).

Назвемо послідовність

d = [x, x + 1, x + 2, ..., x + n - 1]

підходящою, якщо масив можна перетворити у , тобто якщо виконується умова m[i] \le d[i] для всіх від до n - 1.

Залишається знайти найменше значення , при якому послідовність буде підходящою. Це можна зробити за допомогою бінарного пошуку.

Приклад

Розглянемо перший тест. Нехай d = [x, x + 1, x + 2, ..., x + n - 1] — підходяща послідовність з мінімальною сумою елементів.

Очевидно, що m[0] = 3 ≤ x ≤ 11 = max(m[i]).

Бінарним пошуком знаходимо найменше значення , при якому послідовність є підходящою.

Наприклад, при x = 6 послідовність не буде підходящою, а при x = 8 буде.

Розглянемо другий тест. Бінарний пошук запускаємо на проміжку 4 \le x \le 7. Наприклад, вже при x = 4 послідовність є підходящою.

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

Вхідну послідовність зберігаємо в масиві . Послідовність x, x + 1, x + 2, ..., x + n - 1 зберігаємо в масиві .

#define MAX 1000000
int m[MAX], d[MAX];

Функція check генерує в масиві послідовність виду

first, first + 1, first + 2, ..., first + n - 1

Якщо для кожного i~(0 \le i \le n - 1) виконується нерівність m[i] \le d[i], то масив можна отримати з масиву , збільшуючи окремі елементи на . У цьому випадку масив називається підходящим.

Якщо масив є підходящим, то функція check повертає , в іншому випадку — .

int check(int first)
{
  for (int i = 0; i < n; i++)
    d[i] = first++;
  for (int i = 0; i < n; i++)
    if (m[i] > d[i]) return 0;
  return 1;
}

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

scanf("%d", &n);
mx = 0;
for (i = 0; i < n; i++)
{
  scanf("%d", &m[i]);
  if (m[i] > mx) mx = m[i];
}

start = m[0];
end = mx;

Запускаємо бінарний пошук значення в інтервалі [start; end] = [m[0]; mx].

Шукаємо мінімальне значення , при якому послідовність x, x + 1, x + 2, ..., x + n - 1 може бути отримана з масиву m[0], m[1], ..., m[n - 1].

while (start < end)
{
  mid = (start + end) / 2;
  if (check(mid))
    end = mid;
  else
    start = mid + 1;
}

Генеруємо шукану арифметичну прогресію.

for (i = 0; i < n; i++)
  d[i] = start++;

Обчислюємо мінімальну кількість операцій, необхідних для отримання прогресії.

op = 0;
for (int i = 0; i < n; i++)
  op += (d[i] - m[i]);

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

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