矩阵覆盖那点事儿

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;
}



   

<think>好,我现在需要解决用户关于蓝桥杯单片机按键无响应的问题。首先,我得回忆一下常见的按键问题可能出在哪里。根据用户提供的引用资料,特别是引用[1]提到的底层代码和引用[2]的独立按键电路原理,可能的原因包括硬件连接、消抖处理、扫描逻辑或者软件配置的问题。 首先,硬件方面,可能按键接触不良或者电路连接错误。比如独立按键旁边的复位键是否被误触,如引用[2]提到的复位键按下会导致程序重启,所以需要检查是否有短路或误触的情况。另外,矩阵键盘的双按键处理可能在国赛中出现问题,如引用[3]提到的双按键检测,需要确认扫描逻辑是否正确支持多键按下。 软件方面,按键消抖是关键。机械按键在按下和释放时会有抖动,如果没有延时消抖,可能导致检测不稳定。例如,用户可能在代码中没有加入足够的延时,或者在扫描过程中没有正确处理抖动。另外,扫描方式是否正确也很重要,比如行扫描法或列扫描法是否正确配置,导致某些按键未被检测到。 还有可能是IO口的配置问题,比如输入模式是否正确设置为上拉或下拉,是否使能了内部电阻。如果IO口配置错误,可能导致无法正确读取按键状态。此外,中断方式如果使用的话,中断服务程序是否正确编写,是否清除了中断标志位等。 接下来,需要分步骤分析可能的原因,并给出对应的解决方法。首先检查硬件连接,确保按键电路没有问题,包括电阻、线路是否正常。然后检查软件中的消抖处理,是否有足够的延时,或者是否漏掉了消抖步骤。接着,确认按键扫描的逻辑是否正确,特别是矩阵键盘的双按键处理是否在代码中实现。还要检查IO口的配置,确保输入模式正确,并正确读取引脚状态。最后,如果是中断方式,需要检查中断配置和服务程序是否正确。 可能用户的问题出在某个具体的步骤,比如消抖处理不足,或者扫描逻辑错误,导致某些按键未被检测到。例如,当处理双按键时,如果没有正确处理多个按键同时按下的情况,可能会导致无响应。或者,按键扫描的频率过高或过低,导致检测不到按键动作。 总结下来,解决步骤应该是:1. 检查硬件连接;2. 确认消抖处理;3. 验证扫描逻辑;4. 检查IO配置;5. 测试中断配置(如有)。同时,参考提供的引用资料中的内容,如蓝桥杯省赛和国赛中的按键处理要求,确保代码符合比赛的标准。 现在,我需要将这些思考整理成结构化的回答,分列出可能的原因和对应的解决方法,并引用提供的参考资料作为支持。同时,生成相关问题,帮助用户进一步理解问题。</think>### 蓝桥杯单片机按键无响应的原因及解决方法 #### 1. **硬件问题** - **可能原因** - 按键接触不良或线路虚焊,导致信号无法传输[^2]。 - 复位键(RESET)误触或短路,导致程序重启[^2]。 - 矩阵键盘的行/列线未正确连接到单片机IO口。 - **解决方法** - 用万用表检测按键导通性,检查焊接是否牢固。 - 确认复位键未被其他元件短路或误触。 - 对照原理图确认矩阵键盘的行列连接正确[^3]。 #### 2. **消抖处理缺失** - **可能原因** 机械按键按下/释放时存在抖动(约5-10ms),若未消抖会导致多次误触发或无响应[^1]。 - **解决方法** 在按键扫描代码中加入消抖逻辑,例如: ```c if (按键按下) { delay_ms(10); // 延时消抖 if (确认按下) 执行操作; } ``` #### 3. **扫描逻辑错误** - **可能原因** - 单按键扫描未覆盖所有独立按键或矩阵按键。 - 双按键检测逻辑未正确处理(国赛常见问题)[^3]。 - **解决方法** - 检查扫描函数是否遍历所有行和列(矩阵键盘)。 - 双按键场景需同时检测多个按键状态,例如: ```c for (行循环) { 设置当前行为低电平; if (列检测到低电平) 记录按键位置; } ``` #### 4. **IO口配置错误** - **可能原因** - 输入模式未启用上拉电阻,导致引脚电平不稳定。 - 输出模式未正确设置为推挽或开漏。 - **解决方法** - 将按键连接的IO口配置为**输入模式+内部上拉**(参考单片机手册)。 - 矩阵键盘的行线配置为输出,列线配置为输入。 #### 5. **程序逻辑冲突** - **可能原因** - 按键扫描与其他功能(如数码管动态显示)冲突,导致扫描周期过长。 - **解决方法** - 在主循环中保证按键扫描频率(建议10ms一次)。 - 使用定时器中断优化扫描逻辑。 --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值