Площа трикутника зі сторонами a, b, c обчислюється у функції area за формулою Герона:
Нехай — деяка підмножина наявних шматків огорожі. Функція FindSol() знаходить максимальну площу, яку можна огородити за допомогою трикутників, складених із шматків цієї підмножини.
Змінна представляє собою маску підмножини: це -бітове число, -ий біт якого дорівнює , якщо підмножина містить шматок fences[i], і інакше.
Відповіддю на задачу буде значення FindSol(2^n - 1), де — кількість чисел у масиві .
Значення всіх можливих масок лежать в діапазоні від до 2^{16} - 1 (оскільки за умовою є не більше шматків огорожі). У комірці best[mask] зберігається максимальна площа, яку можна огородити підмножиною шматків огорожі, відповідною масці .
Для обчислення FindSol() слід перебрати всі можливі трійки шматків огорожі i, j, k, які присутні в . Для кожної трійки перевіряється нерівність трикутника, щоб переконатися, що з цих шматків можна скласти трикутник. Якщо трикутник можливий, обчислюється сума:
Далі перебираються всі такі трійки (i, j, k), і вибирається та, для якої вказана сума максимальна. Це максимальне значення присвоюється комірці best[mask]:
Якщо спочатку довжини шматків огорожі відсортувати, то при перевірці нерівності трикутника (сторони якого дорівнюють fences[i], fences[j], fences[k]), достатньо перевірити тільки одну умову:
Оскільки при сортуванні виконується нерівність
з цього завжди слідує, що
Приклад
У першому тесті оптимально побудувати трикутники зі сторонами (4, 5, 6) і (7, 8, 9). Сумарна огороджена площа в цьому випадку дорівнює .
У другому тесті відповідь дорівнює , оскільки з огород неможливо скласти жодного трикутника.
У третьому тесті слід побудувати трикутник зі сторонами (4, 4, 4). Зверніть увагу, що довжини огород можуть повторюватися.
Реалізація алгоритму
Оголосимо глобальні змінні.
#define MAX 16
double best[1<<MAX];
int f[MAX+1];Функція area обчислює площу трикутника за допомогою формули Герона.
double area(int a, int b, int c)
{
double p = (a + b + c) / 2.0;
return sqrt(p * (p - a) * (p - b) * (p - c));
}Функція FindSol повертає максимальну площу, яку можна огородити за допомогою трикутників, складених із оград, включених у маску .
double FindSol(int mask)
{Якщо значення в масиві best[mask] більше або дорівнює нулю, воно вже розраховане, і функція повертає це значення. Це дозволяє уникнути повторних обчислень і прискорює роботу алгоритму.
if (best[mask] >= 0.0) return best[mask];Ініціалізуємо best[mask] = 0.
best[mask] = 0;Перебираємо всі можливі трійки індексів (i, j, k) таких, що відповідні огради включені в .
for (int i = 0; i < n - 2; i++) if (mask & (1 << i))
for (int j = i + 1; j < n - 1; j++) if (mask & (1 << j))
for (int k = j + 1; k < n; k++) if (mask & (1 << k))Перевіряємо умову існування трикутника. Оскільки огради спочатку відсортовані, цієї умови достатньо, щоб трикутник існував.
if (f[i] + f[j] > f[k]) Якщо трикутник існує, обчислюємо суму:
площа поточного трикутника area(f[i], f[j], f[k]);
плюс максимальна площа, яку можна огородити залишеними огородами, виключаючи використані в цій трійці:
best[mask] = max(best[mask], area(f[i],f[j],f[k]) +
FindSol(mask ^ (1 << i) ^ (1 << j) ^ (1 << k)));
return best[mask];
}Основна частина програми. Читаємо кількість оград .
while (scanf("%d",&n) == 1)
{
memset(f,0,sizeof(f));Читаємо довжини оград і сортуємо їх за неубуванням.
for (i = 0; i < n; i++) scanf("%d",&f[i]);
sort(f,f + n); Ініціалізуємо масив .
for (i = 0; i < (1 << MAX); i++) best[i] = -1.0;Обчислюємо і виводимо відповідь.
printf("%.4lf\n",FindSol((1 << n) - 1));
}