(poj2663 )用1 * 2 或2 * 1 的小矩形完全覆盖3 * n 的矩形有多少种方案。
显然的,当 n 为奇数的时候无法完全覆盖,即此时的答案是 0;当 n 为偶数的时候,首先我们以 2 为一个单位,每个 3 * 2 的矩形完全覆盖共有 3 个方法,以 4 为一个单位,每个3 * 4 的矩形完全覆盖共有 2 种方法,...依次类推。这样得到方程:F[ i ] = 3 * F[ i - 2 ] + 2 * ( F[ i - 4 ] + F[ i - 6 ] + ... + F[ 0 ])。再进行简化,根据刚才的式子,我们可以得出F[ i - 2 ] = 3 * F[ i - 4 ] + 2 * ( F[ i - 6 ] + ... + F[ 0 ]),所以2 * ( F[ i - 6 ] + F[ i - 8 ] + ... + F[ 0 ] ) = F[ i - 2 ] - 3 * F[ i - 4 ]。代回第一个式子中:F[ i ] = 3 * F[ i - 2 ] + 2 * F[ i - 4 ] + F[ i - 2 ] - 3 * F[ i - 4 ] = 4 * F[ i - 2 ] - F[ i - 4 ]。
特别地,F[ 0 ] = 1。我也不知道为什么,但是当成 0 会 WA,可能是理解不一样:① 0 的时候没用小矩形可放,所以为 0 ;② 要满足 0 的情况应该什么都不放,所以唯一的一个答案就是什么都不放,所以为 1 (自己胡说)。
#include <cstdio>
using namespace std;
const int MAX_N = 35;
int n;
long long f[MAX_N];
void doit()
{
f[0] = 1; f[2] = 3;
for(int i = 4; i <= 30; i += 2) f[i] = 4 * f[i - 2] - f[i - 4];
}
int main()
{
doit();
while(scanf("%d", &n) != EOF){
if(n == -1) break;
printf("%lld\n", f[n]);
}
return 0;
}
(poj2411)用1 * 2 或2 * 1的小矩形覆盖 n * m 的大矩形,有多少种方案。
刚刚 n = 3 时,我们可以人为知道它有哪些状态,但是这是特殊情况,如果n 为任意值呢?显然状态就不止几个,所以要先找出所有可行的状态,用DFS 搜索出来。因为DFS 状态还是一个比较费时的算法,为了加快速度,我们把短的那个当做宽来枚举状态。首先,我们规定:放置横着和竖着的矩形时第i 个格子的状态为1;而竖矩形的上一个和不放置矩形的状态为0;可以画一画进一步理解这个规定。而且因为这是我们人为规定的,所以转移时总能找到下一个合法状态。
画过图基本就可以知道如何转移了(i 为当前行第i 个格子,即第 i 列,now 为当前行的状态,pre 为上一行的状态):1、放置竖矩形,当第i 列为1 时,那么 pre 的第 i 列就得是 0 ( i = i + 1, now << 1 | 1, pre << 1 ); 2、放置竖矩形,当第 i 列为0 时,那么 pre 的第i 列可以为1 ( i = i + 1, now << 1, pre << 1 | 1 ); 3、放置横矩形,因为横矩形占两个格子,且now 和pre 都为1,所以( i = i + 2, now << 2 | 3, pre << 2 | 3); 最后把 now, pre存在一个二维数组中,path[ tot ][ 0 ] = pre,path[ tot ][ 1 ] = now;
然后就要进行行的转移,很简单:设 F[ i ][ path[ j ] ] 表示铺满前 i 行状态为第 j 个的方案数;转移为 F[ i + 1 ][ path[ j ][ 1 ] ] += F[ i ][ path[ j ][ 0 ] ];因为要全部铺满,所以到最后一行时,状态应该全部为 1。所以答案为 F[ n ][ (1 << m) - 1];
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int MAX_N = 15;
int n, m, tot = 0;
long long f[MAX_N][2500];
int path[14000][2];
void dfs(int l, int now, int pre)
{
if(l > m) return;
if(l == m){
path[++ tot][0] = pre; path[tot][1] = now;
return;
}
dfs(l + 1, now << 1 | 1, pre << 1);
dfs(l + 1, now << 1, pre << 1 | 1);
dfs(l + 2, now << 2 | 3, pre << 2 | 3);
}
void doit()
{
memset(path, 0, sizeof(path)); memset(f, 0, sizeof(f));
tot = 0; dfs(0, 0, 0);
f[0][(1 << m) - 1] = 1;
for(int i = 0; i < n; i ++){
for(int j = 1; j <= tot; j ++)
f[i + 1][path[j][1]] += f[i][path[j][0]];
}
printf("%lld\n", f[n][(1 << m) - 1]);
}
int main()
{
while(scanf("%d%d", &n, &m) != EOF){
if(n == 0 && m == 0) break;
if(n < m) swap(n, m);
doit();
}
return 0;
}
用 1 * 2 或 2 * 1 的矩形完全覆盖 4 * n 的矩形的方案数
和上一道题差不多,不同是的是 n 比较大,要用矩阵乘法优化
#include <cstdio>
#include <cstring>
using namespace std;
struct node{
long long mat[20][20];
}a;
int n, m, w;
void dfs(int l, int now, int pre)
{
if(l > w) return;
if(l == w){
a.mat[pre][now] ++;
return;
}
dfs(l + 1, now << 1 | 1, pre << 1);
dfs(l + 1, now << 1, pre << 1 | 1);
dfs(l + 2, now << 2 | 3, pre << 2 | 3);
}
node mul(node a, node b)
{
node c;
memset(c.mat, 0, sizeof(c.mat));
for(int i = 0; i <= 15; i ++)
for(int k = 0; k <= 15; k ++){
if(a.mat[i][k]){
for(int j = 0; j <= 15; j ++)
c.mat[i][j] = (c.mat[i][j] + a.mat[i][k] * b.mat[k][j]) % m;
}
}
return c;
}
node gao(node a, int k)
{
if(k == 1) return a;
node p; memset(p.mat, 0, sizeof(p.mat));
for(int i = 0; i <= 15; i ++) p.mat[i][i] = 1;
if(k == 0) return p;
while(k){
if(k & 1) p = mul(a, p);
a = mul(a, a);
k >>= 1;
}
return p;
}
void doit()
{
if(m == 1) { printf("0\n"); return; }
node ans = gao(a, n);
printf("%lld\n", ans.mat[15][15]);
}
int main()
{
memset(a.mat, 0, sizeof(a.mat));
w = 4; dfs(0, 0, 0);
while(scanf("%d%d", &n, &m) != EOF){
if(n == 0 && m == 0) break;
doit();
}
return 0;
}