15年国赛 铺瓷砖

标题:铺瓷砖

为了让蓝桥杯竞赛更顺利的进行,主办方决定给竞赛的机房重新铺放瓷砖。机房可以看成一个n*m的矩形,而这次使用的瓷砖比较特别,有两种形状,如【图1.png】所示。在铺放瓷砖时,可以旋转。
 
主办方想知道,如果使用这两种瓷砖把机房铺满,有多少种方案。

【输入格式】
输入的第一行包含两个整数,分别表示机房两个方向的长度。

【输出格式】
输出一个整数,表示可行的方案数。这个数可能很大,请输出这个数除以65521的余数。

【样例输入1】
4 4
【样例输出1】
2
【样例说明1】
这两种方案如下【图2.png】所示:
 
【样例输入2】
2 6
【样例输出2】
4
【数据规模与约定】
对于20%的数据,1<=n, m<=5。
对于50%的数据,1<=n<=100,1<=m<=5。
对于100%的数据,1<=n<=10^15,1<=m<=6。
 
资源约定:
峰值内存消耗(含虚拟机) < 512M
CPU消耗  < 8000ms

求解使用两种瓷砖铺满一个n*m的区域的方案数。
对于小范围的数据,可以使用搜索求解。也可以使用搜索在自己的电脑上先算出方案数,将这些答案保存在程序中,直接输出答案即可。
对于稍微大一点的数据,可以使用状态压缩的动态规划来求解。有两种瓷砖,状态的转移比较复杂,
对于100%的数据,需要将在状态压缩的动态规划中去除无用状态,然后建立矩阵表示,使用矩阵乘法的方法来实现状态压缩中的递推。

//瓷砖铺放
#include <iostream>
#include <cstdlib>
#include <cstdio>
#include <cstring>
using namespace std;

const int MOD = 65521;

const int MM = 800;
const int RSIZE = 128;

const int MATRIX_SIZE = 256;

class Matrix {
public:
	unsigned int s[MATRIX_SIZE][MATRIX_SIZE];
};

long long n;
int m, M;
int tt[MM][RSIZE], fr[MM][RSIZE];
int *cur[MM], *curFr[MM];
int tc[MM], nc[MM];
Matrix tm, dst, tmpm;
bool mark[MM], canReach[MM];
int remap[MM], who[MM];

void search(int cp, int cl, int nl)
{
	if (cp > m)
		return ;
	if (cp == m)
	{
		*(cur[cl]++) = nl;
		*(curFr[nl]++) = cl;
		return ;
	}
	search(cp+1, cl*3+1, nl*3);
	search(cp+1, cl*3+2, nl*3+1);

	// (1) 
	search(cp+2, cl*3*3+1, nl*3*3+3+1);
	search(cp+3, cl*3*3*3, nl*3*3*3+9+3+1);
	search(cp+4, cl*3*3*3*3, nl*3*3*3*3+27+9+3);
	// (2)
	search(cp+2, cl*3*3, nl*3*3+1);
	// (3)
	search(cp+2, cl*3*3, nl*3*3+3);
	// (4)
	if (cp>0 && nl%3==0)
		search(cp+1, cl*3, nl*3+3+1);
	// (5)
	search(cp+2, cl*3*3+1, nl*3*3+3*2+1);
	search(cp+3, cl*3*3*3, nl*3*3*3+9*2+3+1);
	search(cp+4, cl*3*3*3*3, nl*3*3*3*3+27*2+9+3);
	// (6)
	search(cp+3, cl*3*3*3, nl*3*3*3+3);
	// (7)
	if (cp>0 && nl%3==0)
		search(cp+1, cl*3, nl*3+3+2);
	// (8)
	if (cp>0 && nl%3==0)
	{
		search(cp+2, cl*3*3+1, nl*3*3+9+3+1);
		search(cp+3, cl*3*3*3, nl*3*3*3+27+9+3+1);
		search(cp+4, cl*3*3*3*3, nl*3*3*3*3+81+27*2+9+3);
	}
}
void goto0(int x)
{
	if (canReach[x])
		return ;
	canReach[x] = true;
	for (int *pp = fr[x]; pp < curFr[x]; ++pp)
		goto0(*pp);
}

int ccnt = 0;
void go(int x)
{
	if (!canReach[x])
		return ;
	if (mark[x])
		return ;
	who[ccnt] = x;
	remap[x] = ccnt++;
	mark[x] = true;
	for (int *pp = tt[x]; pp < cur[x]; ++pp)
		go(*pp);
}
void pr3(int x, int w)
{
	if (w > 1)
		pr3(x/3, w-1);
	cout << x%3;
}
void prepare()
{
	memset(tt, 0, sizeof(tt));
	for (int i = 0; i < M; ++i)
	{
		cur[i] = tt[i];
		curFr[i] = fr[i];
	}
	search(0, 0, 0);

	int maxR = 0;
	for (int i = 0; i < M; ++i)
		if (cur[i]-tt[i] > maxR)
			maxR = cur[i] - tt[i];

	maxR = 0;
	for (int i = 0; i < M; ++i)
		if (curFr[i]-fr[i] > maxR)
			maxR = curFr[i] - fr[i];

	memset(canReach, 0, sizeof(canReach));
	goto0(0);
	
	memset(mark, 0, sizeof(mark));
	go(0);

	memset(&tm, 0, sizeof(tm));
	for (int i = 0; i < M; ++i)
		if (mark[i])
			for (int *pp = tt[i]; pp < cur[i]; ++pp)
				if (mark[*pp])
					tm.s[remap[i]][remap[*pp]]++;
}
void matrixMul(Matrix &dst, const Matrix &a, const Matrix &b)
{
	for (int i = 0; i < ccnt; ++i)
		for (int j = 0; j < ccnt; ++j)
		{
			long long r = 0;	
			const unsigned int *ai = a.s[i];
			for (int k = 0; k < ccnt; ++k)
				r += ai[k] * b.s[k][j];
			r %= MOD;
			dst.s[i][j] = r;
		}
}
long long getAns()
{
	long long cur = 1;
	memset(&dst, 0, sizeof(dst));
	for (int i = 0; i < ccnt; ++i)
		dst.s[i][i] = 1;
	while (n)
	{
		if (n&cur)
		{
			matrixMul(tmpm, dst, tm);
			dst = tmpm;
			n -= cur;
		}
		matrixMul(tmpm, tm, tm);
		tm = tmpm;
		cur *= 2;
	}
	return dst.s[0][0];
}
int main()
{
	cin >> n >> m;
	M = 1;
	for (int i = 0; i < m; ++i)
		M *= 3;
	prepare();
	cout << getAns() << endl;
	return 0;
}

 

### 蓝桥瓷砖问题描述 蓝桥瓷砖问题是典型的动态规划类题目,通常涉及如何用特定形状的砖块设矩形区域。这类问题的核心在于寻找最优解或者计算可能的方案总数。 #### 问题背景 假设有一个大小为 $ n \times m $ 的矩形地面,需要用若干种固定尺寸的瓷砖完全覆盖该地面而不重叠。每种瓷砖可以旋转或翻转放置。目标是求出有多少种不同的方式能够完成这一任务[^1]。 --- ### 算法分析 此类问题可以通过 **状态压缩动态规划** 或者 **记忆化搜索** 来解决。以下是具体的算法设计: #### 动态规划建模 定义状态 `dp[i][s]` 表示当前处理到第 i 行,并且前 i-1 行已经全部被填充完毕的情况下,第 i 行的状态为 s 时的不同法数目。其中 s 是一个二进制掩码,表示第 i 行哪些位置已经被占用。 转移方程的关键在于枚举所有合法的瓷砖摆放方式来新 dp 值。对于每一个新的状态 t(即新加入的一行),如果它能与上一行剩余部分形成有效的组合,则有: $$ dp[i][t] += dp[i-1][prev\_state] $$ 边界条件设置为当没有任何未覆盖的位置时初始值设为 1 即可满足递推关系链路闭合需求[^2]。 --- ### 解题思路详解 1. **输入解析** 输入数据一般包括矩阵维度 N 和 M ,以及可用瓷砖的具体规格列表。 2. **预处理** 对于给定类型的每一块砖头预先计算其所有可行布局形式并存储起来以便后续快速查询调用。 3. **核心逻辑实现** 使用 DFS 加缓存机制遍历整个棋盘空间尝试各种可能性直到找到最终答案为止;也可以采用迭代版本基于上述提到过的 DP 数组逐步构建解决方案路径图谱。 4. **输出结果** 根据不同题目要求返回总方案数量或者是具体某一种排列展示实例等等相关内容信息。 ```python def count_ways(n, m): MOD = int(1e9 + 7) # Initialize the state transition matrix. states = [] def generate_states(mask, pos): if pos == m: states.append(mask) return # Place a vertical tile or leave it empty. if not mask & (1 << pos): generate_states(mask | (1 << pos), pos + 1) # place vertically # Try placing horizontal tiles where possible. if pos < m - 1 and not (mask & (1 << pos)) and not (mask & (1 << (pos + 1))): new_mask = mask | (1 << pos) | (1 << (pos + 1)) generate_states(new_mask, pos + 2) # place horizontally generate_states(mask, pos + 1) # skip this position generate_states(0, 0) num_states = len(states) prev_dp = [0]*num_states curr_dp = [0]*num_states start_state_index = states.index(0) prev_dp[start_state_index] = 1 for row in range(n): for si in range(num_states): s = states[si] if not prev_dp[si]: continue for sj in range(num_states): next_s = states[sj] combined = s | next_s if bin(combined).count('1') != m: continue curr_dp[sj] = (curr_dp[sj] + prev_dp[si]) % MOD prev_dp, curr_dp = curr_dp, [0]*num_states end_state_index = states.index(0) return prev_dp[end_state_index] print(count_ways(3, 4)) # Example usage with dimensions 3x4 grid ``` --- ### 总结 通过以上方法论探讨可知,在面对诸如“蓝桥杯竞赛中的复杂场景应用型考题之时,我们应当灵活运用所学专业知识技巧加以应对解答。此案例充分体现了计算机科学领域内关于离散结构优化方面的精髓所在[^3]。
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值