628. Доминошки
Дана
прямоугольная доска m × n. Найдите количество способов замостить
её доминошками – прямоугольниками размера 1 × 2 клетки.
Вход. Два числа m и n (1 ≤ m, n ≤ 10).
Выход. Выведите
количество способов, которыми можно замостить доску заданного размера
доминошками.
Пример
входа |
Пример
выхода |
2 3 |
3 |
динамическое
программирование
Пусть
прямоугольная доска имеет m строк и n колонок.
Профилем будем
называть столбец. Хранить его будем в виде двоичной маски. В качестве состояния
динамики будем использовать профили размера m. В этом профиле
1 означает, что
домино лежит горизонтально и заканчивается на этом столбце, иначе 0. Таких профилей
будет 2m.
Пусть d[i][j] = 1, если из профиля i можно перейти в j-ый, иначе d[i][j] = 0.
Пусть a[i][p1] – количество
способов замощения первых i – 1
столбцов и заканчивающийся на профиле p1. Тогда
a[i][p1] =
Ответом будет a[n + 1][0].
Реализация алгоритма
Объявим рабочие
массивы:
·
d – таблица переходов: d[i][j] = 1, если из профиля i можно перейти в профиль j. Иначе d[i][j]
= 0.
·
a – результирующая матрица: a[i][p1] равно количеству замощения первых i – 1 столбцов, заканчивающихся профилем
p1.
#define MAX 10
int d[1<<MAX][1<<MAX];
long long
a[MAX+2][1<<MAX];
Возвращает значение pos-бита в числе x.
int bit(int
x, int pos)
{
if (pos <
0) return 0;
return (x
& (1 << pos));
}
Генерирует все профили p2, в которые
можно попасть из p1. Далее устанавливаем d[p1][p2] = 1.
void go(int
p1, int p2, int
pos)
{
Профиль p1 просмотрен до конца, до m-ой позиции. Значит из p1 можно получить p2.
if (pos == m)
{
d[p1][p2] = 1;
return;
}
Если ячейка (бит) pos в профиле p1 не занятый.
if
(bit(p1,pos) == 0)
{
Тогда кладем горизонтальное
домино.
go(p1,p2 + (1LL << pos), pos+1);
Если не занята и
следующая, (pos + 1)-ая ячейка, то
ложим вертикальную доминошку. При этом в профиль p2 ничего не
добавляем, увеличиваем pos на 2.
if (pos
< m - 1)
if
(bit(p1,pos+1) == 0) go(p1,p2,pos+2);
}
else
Ячейка pos в
профиле p1 занята, увеличиваем на 1 позицию
pos в профиле p1.
go(p1,p2,pos+1);
}
Основная часть программы. Читаем входные данные.
scanf("%d %d",&m,&n);
memset(d,0,sizeof(d));
memset(a,0,sizeof(a));
Заполнение таблица переходов d. Для каждого профиля i находим все профили, в которые можно перейти.
for(i = 0; i < (1 << m); i++)
go(i,0,0);
Пересчитываем результирующую матрицу.
a[1][0] = 1;
for(i = 2; i
<= n + 1; i++)
for(p1 = 0;
p1 < (1 << m); p1++)
{
a[i][p1] = 0;
for(p2 =
0; p2 < (1 << m); p2++)
a[i][p1] += a[i-1][p2] * d[p2][p1];
}
Выводим ответ.
printf("%lld\n",a[n+1][0]);