Возле виллы
Пемберли, расположенной в южном районе Байтляндии, находится большое пастбище.
Миссис Дарси беспокоится о своих нежных растениях, которые могут быть вытоптаны
незнакомцами. Поэтому она решила оградить на пастбище некоторые участки земли
треугольными заборами.
В подвале у
миссис Дарси имеется несколько оград для забора. Каждую треугольную область она
формирует, используя ровно три ограды, так что каждая сторона треугольника
соответствует одной ограде. Ограды достаточно прочные и красивые, поэтому она
не будет соединять несколько оград, чтобы получить одну сторону, или разрезать одну
ограду на более короткие части. Цель миссис Дарси — оградить как можно большую
суммарную площадь пастбища.
Вход. Каждая строка содержит отдельный тест. Первое число в строке – количество оград n (n
≤ 16),
имеющихся у миссис Дарси. Далее следуют n целых чисел от 1 до 100 – длины этих оград.
Выход. Для каждого теста выведите в отдельной строке
максимальную площадь, которую можно оградить с использованием имеющихся оград.
Ответ необходимо вывести с точностью до 4 десятичных знаков.
|
Пример входа |
Пример выхода |
|
7 3 4 5 6 7 8 9 4 1 2 4 8 4 7 4 4 4 |
36.7544 0.0000 6.9282 |
РЕШЕНИЕ
динамическое программирование - маски
Анализ алгоритма
Площадь треугольника со сторонами a, b, c вычисляется
в функции area по формуле Герона:
S =
, где p = ![]()
Пусть P – некоторое подмножество имеющихся кусков забора.
Функция FindSol(mask) находит максимальную площадь, которую можно оградить с
помощью треугольников, составленных из кусков этого подмножества.
Переменная mask представляет собой маску подмножества: это 16-битовое число, i-ый бит
которого равен 1, если подмножество P
содержит кусок fences[i], и 0 иначе.
Ответом на задачу будет значение FindSol(2n – 1), где n – количество чисел в массиве fences.
Значения всех возможных масок mask лежат в диапазоне от 0 до 216 – 1 (так как по условию имеется не
более 16 кусков забора). В ячейке best[mask]
хранится максимальная площадь,
которую можно оградить подмножеством кусков забора, соответствующим маске mask.
Для вычисления FindSol(mask)
следует перебрать все возможные тройки кусков забора i, j, k, которые
присутствуют в mask. Для каждой тройки
проверяется неравенство треугольника, чтобы убедиться, что из этих кусков можно
составить треугольник. Если треугольник возможен, вычисляется сумма:
area(fences[i],
fences[j], fences[k]) + FindSol(mask \ {i, j, k})
Далее перебираются все такие тройки (i, j, k), и выбирается та, для которой указанная сумма максимальна. Это
максимальное значение присваивается ячейке best[mask]:
FindSol(mask) =
![]()
Если изначально
длины кусков забора отсортировать, то при проверке неравенства треугольника
(стороны которого равны fences[i],
fences[j], fences[k]), достаточно проверить только одно
условие:
fences[i] + fences[j] > fences[k]
Поскольку при сортировке
выполняется неравенство
fences[i] £ fences[j] £ fences[k],
из этого всегда следует,
что
fences[i] + fences[k] > fences[j] и
fences[j] + fences[k] > fences[i].
Пример
В первом тесте оптимально построить треугольники со
сторонами (4, 5, 6) и (7, 8, 9). Суммарная ограждённая площадь в этом случае
равна 36.7544.
Во втором тесте ответ равен 0, так как из оград невозможно
составить ни одного треугольника.
В третьем тесте следует построить треугольник со сторонами
(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
возвращает максимальную площадь, которую можно оградить с помощью треугольников, составленных из оград, включённых в маску mask.
double FindSol(int mask)
{
Если значение в
массиве best[mask] больше или равно нулю, оно уже рассчитано, и функция
возвращает это значение. Это позволяет избежать повторных вычислений и ускоряет
работу алгоритма.
if (best[mask] >= 0.0) return
best[mask];
Инициализируем best[mask]
= 0.
best[mask] = 0;
Перебираем все возможные тройки индексов (i, j, k)
таких, что соответствующие ограды включены в mask.
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]);
·
плюс максимальная площадь,
которую можно оградить оставшимися заборами, исключая использованные в этой
тройке:
FindSol(mask ^ (1 << i) ^ (1 << j)
^ (1 << k))
best[mask] = max(best[mask], area(f[i],f[j],f[k]) +
FindSol(mask ^ (1 <<
i) ^ (1 << j) ^ (1 << k)));
return best[mask];
}
Основная часть
программы. Читаем количество оград n.
while (scanf("%d",&n)
== 1)
{
memset(f,0,sizeof(f));
Читаем длины
оград и сортируем их по неубыванию.
for (i = 0; i < n; i++) scanf("%d",&f[i]);
sort(f,f + n);
Инициализируем
массив best.
for (i = 0; i < (1 << MAX); i++) best[i] =
-1.0;
Вычисляем и
выводим ответ.
printf("%.4lf\n",FindSol((1 << n) - 1));
}