[luogu p1990] 覆盖墙壁

传送门

覆盖墙壁

题目描述

你有一个长为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\times 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\times 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}\)

总结:

\[\begin{cases} f_n = f_{n - 1} + f_{n - 2} + 2 \times g_{n - 2} \\ g_n = f_{n - 1} + g_{n - 1} \end{cases} \]

这是两个数组的递推式结果。其实本题到这里本应该结束的,但是我们难道就不能压成一个数组了吗?

\(g_n\)的递推式。

\[g_n = f_{n - 1} + g_{n - 1}\\ g_n = f_{n - 1} + f_{n - 2} + g_{n - 2}\\ g_n = f_{n - 1} + f_{n - 2} + f_{n - 3} + \ldots + f_1 + f_0\\ g_n = \sum_{i = 0} ^ {n - 1}f_i = \sum_{i = 1} ^ {n - 1} + 1 \]

原式等同于:

\[f_n = f_{n - 1} + f_{n - 2} + 2 \times \sum_{i = 1} ^ {n - 3}f_i + 2 \]

然后让我施展一点魔法……让\(n \rightarrow n - 1\)

这样就可以得到:

\[f_{n - 1} = f_{n - 2} + f_{n - 3} + 2\times \sum_{i = 1} ^ {n - 4}f_i + 2 \]

对比这两个式子:

\[f_n = f_{n - 1} + f_{n - 2} + 2 \times \sum_{i = 1} ^ {n - 3}f_i + 2 \\ f_{n - 1} = f_{n - 2} + f_{n - 3} + 2\times \sum_{i = 1} ^ {n - 4}f_i + 2 \]

上式减下式,得到:

\[f_n - f_{n - 1} = f_{n - 1} + f_{n - 2} + 2 \times \sum_{i = 1} ^ {n - 3}f_i + 2 - (f_{n - 2} + f_{n - 3} + 2\times \sum_{i = 1} ^ {n - 4}f_i + 2) \]

将等号右边的式子整理一下,

\[f_n - f_{n - 1} = f_{n - 1} + f_{n - 3} \]

然后把左边的\(- f_{n - 1}\)移项,得到最终的递推式:

\[f_n = 2 \times f_{n - 1} + f_{n - 3} \]

边界条件:

\[f_1 = 1\\ f_2 = 2\\ f_3 = 5 \]

推出了式子以后,代码就很好写了。

代码

/*
 * @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

posted @ 2020-04-15 15:21  东北小蟹蟹  阅读(260)  评论(0编辑  收藏  举报