覆盖墙壁
题目描述
你有一个长为N宽为2的墙壁,给你两种砖头:一个长2宽1,另一个是L型覆盖3个单元的砖头。如下图:
0 0
0 00
砖头可以旋转,两种砖头可以无限制提供。你的任务是计算用这两种来覆盖N*2的墙壁的覆盖方法。例如一个2*3的墙可以有5种覆盖方法,如下:
012 002 011 001 011
012 112 022 011 001
注意可以使用两种砖头混合起来覆盖,如2*4的墙可以这样覆盖:
0112
0012
给定N,要求计算2*N的墙壁的覆盖方法。由于结果很大,所以只要求输出最后4位。例如2*13的覆盖方法为13465,只需输出3465即可。如果答案少于4位,就直接输出就可以,不用加0,如N=3,时输出5。
输入输出格式
输入格式
一个整数N(1<=N<=1000000),表示墙壁的长。
输出格式
输出覆盖方法的最后4位,如果不足4位就输出整个答案。
输入输出样例
输入#1
13
输出#1
3465
分析
此题不用多说,动态规划。但是这道题还是比较难讲的,参考了一下题解才想出来一种比较易懂的讲解?QAQ233
设(f_i)为(2 imes i)尺寸墙壁的恰好覆盖方法。我们规定,(f_0 = 1)!!
来看(n = 4)的情况⑧,现在一个都没填充:
0000
0000
首先不考虑L
形瓷砖。
如果瓷砖的最后两格横着铺上了一形瓷砖,也就是这样:
0001
0001
此时的方法数就是除去最后一列的子问题,也就是(f_{i-1})。
还有一种情况是瓷砖的最后四个竖着铺上了一形瓷砖,也就是这样:
0011
0022
注意哈是横着铺。竖着铺的情况已经相当于第一种了。方法数是除去最后两列的子问题,也就是(f_{i - 2})。
答案就是两种情况的综合,也就是(f_i = f_{i - 1} + f_{i - 2})。
现在一字型瓷砖已经被我们讨论完了,再来讨论L
形瓷砖。
此时再设一个数组,设(g_i)为既要覆盖上(2 imes i)尺寸墙壁,也要恰好多出一块的覆盖方法。规定(g_0 = 0)哦!
先在最后铺一个:
0001
0011
然后反向思维,不看铺上的瓷砖,看空地。此时先补上前(n - 2)列的墙壁,然后再补多出的一个。也就是说,这种情况的方案数是(g_{n - 2})。
但是L
形瓷砖可以反过来使用,也就是这样:
0011
0001
此时的方案数仍然是(g_{n - 2}),所以实际上我们要统计两次(g_{n - 2})。
但是(g)数组我们怎么进行一个维护呢?首先对于突出一个方块的,我们有两种方法覆盖。
第一种:再补一个L,补成长方形:
0221
0211
当然
0211
0221
也是一样的道理。
此时的方法数显然就是(f_{n - 3})辣。
第二种:继续补长方形,仍然多出一块
0221
0011
虽然这看着非常难受,强迫症甚至可以为此砸掉电脑,但是这种情况完全有可能啊!(哭)
这种情况下仍然还需要(g)数组,不过相当于又填补了一列(虽然刚刚实际上是填补了一个横向的,但是把刚刚少的那一块填上了两块,现在又多出了一块,整体还是多了一列的,自己看看图就知道了)。
这种情况的方案数是(g_{n - 3})。
也就是说,(g_{n - 2} = f_{n - 3} + g_{n - 3}),也就等同于(g_n = f_{n - 1} + g_{n - 1})。
总结:
这是两个数组的递推式结果。其实本题到这里本应该结束的,但是我们难道就不能压成一个数组了吗?
看(g_n)的递推式。
原式等同于:
然后让我施展一点魔法……让(n ightarrow n - 1)
这样就可以得到:
对比这两个式子:
上式减下式,得到:
将等号右边的式子整理一下,
然后把左边的(- f_{n - 1})移项,得到最终的递推式:
边界条件:
推出了式子以后,代码就很好写了。
代码
/*
* @Author: crab-in-the-northeast
* @Date: 2020-04-14 22:11:54
* @Last Modified by: crab-in-the-northeast
* @Last Modified time: 2020-04-15 15:19:02
*/
#include <iostream>
#include <cstdio>
const int maxn = 1000005;
const int mod = 10000;
int dp[maxn] = {0, 1, 2, 5};
int main() {
int n;
std :: cin >> n;
if(n <= 3) {
std :: cout << dp[n] << std :: endl;
return 0;
}
for(int i = 4; i <= n; i++)
dp[i] = (dp[i - 1] * 2 % mod + dp[i - 3] % mod) % mod;
std :: cout << dp[n] << std :: endl;
return 0;
}
评测结果
AC 100:R32801187