矩阵覆盖那点事儿

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 * 22 * 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、放置横矩形,因为横矩形占两个格子,且nowpre 都为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 * 22 * 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;
}



   

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值