Матч
395, Небоскребы (Skyscrapers)
Дивизион 1,
Уровень 2
Линия горизонта в городе содержит
n зданий, каждое из которых имеет
уникальную высоту от 1 до n. Дом
виден слева (справа), если левее (правее) его нет дома с большей высотой.
Например, если дома имеют порядок {1, 3, 5, 2, 4}, то слева видны дома 1, 3, 5,
а справа – 4 и 5. Известно, что дома
расположены так, что слева видно в точности leftSide
домов, а справа – rightSide. Найти количество перестановок домов, для которых это
возможно. Результат вернуть по модулю 1000000007.
Класс: Skyscrapers
Метод: int
buildingCount(int n, int leftSide, int rightSide)
Ограничения: 1 £ n £ 100, 1 £
leftSide, rightSide £ n.
Вход. Количество зданий n, значения leftSide и rightSide.
Выход. Количество перестановок n
домов, для которых слева видно в точности leftSide,
а справа – rightSide домов.
Пример входа
n |
leftSide |
rightSide |
3 |
2 |
2 |
5 |
3 |
2 |
8 |
3 |
2 |
Пример выхода
2
18
4872
РЕШЕНИЕ
комбинаторика + рекурсия
Предположим, что осталось
расположить n домов, которые следует
расположить так, чтобы слева было видно leftSide,
а справа – rightSide домов. Пусть это можно сделать f(n, leftSide, rightSide) способами. Рассмотрим дом с
наименьшей высотой. Если его поставить слева, то он будет всегда видим, а
оставшиеся дома можно будет расставить f(n
– 1, leftSide – 1, rightSide) способами. Если дом с
наименьшей высотой поставить справа, то он всегда будет оставаться видимым
справа, остальные дома можно будет расставить f(n – 1, leftSide, rightSide – 1) способами. Не с краю наименьший
дом можно поставить n – 2 способами.
В этом случае он не будет видим, поэтому оставшиеся дома можно расставить (n – 2) * f(n – 1, leftSide, rightSide) способами. Заметим, что при n = 1 единственная возможная расстановка
будет лишь при leftSide = rightSide = 1. Получаем рекуррентное
соотношение:
f(n, leftSide, rightSide) = f(n – 1, leftSide – 1, rightSide) + f(n – 1, leftSide, rightSide – 1) +
(n – 2) * f(n – 1, leftSide, rightSide),
f(1, 1, 1) = 1,
f(1, x, y) = 0, когда x и y
одновременно не равны 1
ПРОГРАММА
#include <stdio.h>
#include <memory.h>
#define MAX 101
int dp[MAX][MAX][MAX];
int f(int n, int leftSide, int
rightSide)
{
if (n == 1) return
(leftSide == 1 && rightSide == 1) ? 1 : 0;
if ((leftSide < 1) || (rightSide < 1)) return 0;
if (dp[n][leftSide][rightSide] != -1) return dp[n][leftSide][rightSide];
dp[n][leftSide][rightSide] = (int)(((long long)f(n-1,leftSide-1,rightSide)
+
(long long)f(n-1,leftSide,rightSide-1)
+
(n-2)*(long long)f(n-1,leftSide,rightSide)) % 1000000007);
return dp[n][leftSide][rightSide];
}
class Skyscrapers
{
public:
int buildingCount(int
n, int leftSide, int
rightSide)
{
memset(dp,-1,sizeof(dp));
return f(n,leftSide,rightSide);
}
};