Палички#1405

Палички (Розбір)

Нехай — даний набір паличок. Необхідно знайти два непересічних підмножини і з , такі, щоб палички кожного з них можна було розбити на дві підмножини з рівною сумою довжин.

Для кожного підмножини mask \subseteq S обчислимо суму довжин його паличок і запишемо в sum[mask].

Переберемо всі підмножини I \subset S. Для кожної підмножини переберемо всі його підмножини J \subset I. Якщо існує таке , що 2 \cdot sum[J] = sum[I] (тобто сумарна довжина паличок в вдвічі менша від суми довжин усіх паличок з ), то підмножина можна використовувати в якості протилежних сторін прямокутника. У цьому випадку встановимо can[I] = true.

Вся описана процедура виконується за O(3^n).

Переберемо всі підмножини множини паличок. Якщо для деякого множини існує підмножина J \subset I таке, що can[J] = true і can[I~xor~J] = true (тобто множини і I~xor~J не перетинаються), то тим самим отримуємо один з можливих розподілів паличок між горизонтальними і вертикальними сторонами прямокутника.

Довжини сторін такого прямокутника дорівнюють sum[J] / 2 і sum[I~xor~J] / 2. Серед усіх знайдених варіантів обираємо прямокутник з максимальною площею.

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

Довжини паличок зберігаються в масиві . Для кожного підмножини паличок зберігаємо їхню сумарну довжину в масиві sum[mask]. Значення can[mask] встановлюємо в , якщо множина можна поділити на дві підмножини з рівною сумою довжин, які відповідатимуть протилежним сторонам прямокутника.

#define MAX 16
int a[MAX];
int sum[1 << MAX];
bool can[1 << MAX];

Основна частина програми. Читаємо вхідні дані.

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

Переберемо всі підмножини паличок і обчислимо сумарну довжину кожного з них.

for (i = 0; i < 1 << n; i++)
  for (j = 0; j < n; j++)
    if (i & (1 << j)) sum[i] += a[j];

Далі переберемо всі можливі підмножини множини паличок. Якщо для деякого множини існує підмножина J \subset I таке, що сумарна довжина паличок в вдвічі менша сумарної довжини паличок в , то встановимо can[I] = true. У цьому випадку палички з множини можна використовувати як протилежні сторони прямокутника.

for (i = 1; i < 1 << n; i++) 
{
  int s = sum[i];
  for (j = i; j > 0; j = (j - 1) & i)
    if (sum[j] * 2 == s) 
    {
      can[i] = true;
      break;
    }
}

Переберемо всі підмножини множини паличок. Якщо для деякого множини існує підмножина J \subset I таке, що can[J] = true і can[I~xor~J] = true, то отримуємо один з можливих розподілів паличок між горизонтальними і вертикальними сторонами прямокутника. Довжини сторін такого прямокутника дорівнюють sum[J] / 2 і sum[I xor J] / 2.

res = 0;
for (i = 1; i < 1 << n; i++)
  for (j = i; j > 0; j = (j - 1) & i)
    if (can[j] && can[i ^ j])
      res = max(res, sum[j] / 2 * 1LL * sum[i ^ j] / 2);

Виводимо площу знайденого прямокутника максимального розміру.

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