Розрізання палки#11261

Розрізання палки (Розбір)

Нехай масив містить точки розпилу: m_1, ..., m_n. Додамо початкову і кінцеву точки: m_0 = 0, m_{n+1} = l.

Нехай функція f(a, b) повертає мінімальну вартість розпилу відрізку палиці від m_a до m_b з урахуванням необхідних точок розпилу, що знаходяться всередині відрізка. Усередині відрізка [m_a; m_{a + 1}] точок розпилу немає, тому f(a, a + 1) = 0. Відповіддю на задачу буде значення f(0, n + 1). Розраховані значення f(a, b) зберігаємо в осередках масиву .

Нехай відрізок палиці [m_a, m_b] слід розпиляти на кілька частин. Якщо перший розпил ми здійснимо в точці m_i~(a < i < b), то вартість самого розпилу складе m_b - m_a. Далі слід розпиляти залишені шматки відповідно за ціною f(a, i) і f(i, b). Значення слід вибрати так, щоб мінімізувати загальну вартість:

f(a, b) = \min_{\substack{a<i<b}}(f(a,i) + f(i,b)) + m_b - m_a

Приклад

Розглянемо другий тест. Наведемо один з можливих методів розпилу палиці довжини вартістю 10 + 7 + 3 + 3 = 23:

Розглянемо оптимальний метод розпилу вартістю 10 + 6 + 3 + 3 = 22:

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

Оголосимо константи та робочі масиви.

#define MAX 55
#define INF 0x3F3F3F3F
int dp[MAX][MAX];
int m[MAX];

Функція f(a, b) повертає мінімальну вартість розрізу шматка палиці на відрізку з урахуванням точок розпилу, що знаходяться всередині цього відрізка.

int f(int a, int b)
{

Якщо a + 1 = b, то всередині відрізка точок розрізу немає. Повертаємо .

  if (b - a == 1) return 0;

Якщо значення f(a, b) вже обчислено, то повертаємо його.

  if (dp[a][b] != INF) return dp[a][b];

Здійснюємо розріз у точці i~(a < i < b). Обчислюємо вартість наступним чином:

f(a, b) = \min_{\substack{a<i<b}}(f(a,i) + f(i,b)) + m_b - m_a
  for (int i = a + 1; i < b; i++)
    dp[a][b] = min(dp[a][b], m[b] - m[a] + f(a, i) + f(i, b));

Повертаємо результат f(a, b) = dp[a][b].

  return dp[a][b];
}

Основна частина програми. Читаємо вхідні дані для кожного тесту.

while(scanf("%d",&l),l)
{
  scanf("%d",&n);

Додамо початкову і кінцеву точки розпилу: m_0 = 0, m_{n+1} = l.

  m[0] = 0; m[n+1] = l;
  for(i = 1; i <= n; i++)
    scanf("%d",&m[i]);

Ініціалізуємо масив .

  memset(dp,0x3F,sizeof(dp));

Обчислюємо і виводимо відповідь.

  printf("The minimum cutting is %d.\n",f(0,n+1));
}