剑指OFFER 矩形覆盖
Posted virgildevil
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了剑指OFFER 矩形覆盖相关的知识,希望对你有一定的参考价值。
剑指OFFER 矩形覆盖
题目描述
我们可以用2*1的小矩形横着或者竖着去覆盖更大的矩形。请问用n个2*1的小矩形无重叠地覆盖一个2*n的大矩形,总共有多少种方法?
分析
一般我们遇到这种看起来计算量很大似乎需要穷举的时候,都会测试一下是否满足动态规划的条件.
测试时先假设满足该问题满足最优子结构
我们先手动算出n=1,2,3,4,5的答案以便对我们的推理进行验证
被覆盖的2*1矩形个数 | 覆盖的种数 |
---|---|
1 | 1 |
2 | 2 |
3 | 3 |
4 | 5 |
5 | 8 |
现在举n=3时的例子,n=3时,最多有3种覆盖方法
如下图所示
如果我们再加上一块(常识告诉我们只有两种添加的方法,横着或者竖着),我们先看横着放的,如图所示
如果这么放的话,我们就能得到n=4时一定有一部分是来自n=3时的覆盖数
接下来我们看竖着放,如图所示,明显看到竖着放的话至少需要两个砖块.
那么这么放的话,我们就能得到n=5时一定有一部分是来自n=3时的覆盖数
我们再把思维转个弯,把n=3时的情况,去掉一块,再去掉两块的情况也是和上面的一模一样的.
我们为了算出n=3时的方法种数,那么上面的说法就变成了n=3时一定是由n=2和n=1的种数得来的,即
n3的种数=n2的种数+n1的种数
推导到所有的n
f(n)=f(n-1)+f(n-2)
得出该公式后,代入前面手动算出的结果进行验证,发现完全吻合,接下来写出代码,直接通过了.
代码
class Solution {
public:
int rectCover(int number) {
vector<int> dp;
dp.resize(number+1);
dp[0] = 0;//dp[0]无意义
dp[1] = 1;
dp[2] = 2;
for(int i=3;i<=number;i++)
{
dp[i] = dp[i-1] + dp[i-2];
}
return dp[number];
}
};
一些问题
但是,可能还有一些问题需要推敲一下
为什么我只放了一块,或者说两块,而不是三块或者四块呢?
这很简单,你放三块不就等于先放一块,再放两块吗?四块同理
那又有问题了,为什么两块不等于放两次一块的呢?
这就需要细心思考了,放一块的时候是横着放的,放两块的时候是竖着放的,这两种做法是不等同的(虽然放两块横的确实是相当于放两次一块横的).
放一块横的或者放两块竖的是从某一个状态进入到下一个状态的最小代价,最小代价确定了就能写出方程了.
如何证明是最优子结构?
上面的推理都基于某一个状态的结果是由上一个的最优结果得到的.即最优子结构.这个说真的要严谨地证明出来还挺难的,但是从直觉上也能感受到其正确性,假设你要让鱼塘充满水,那么你肯定会让鱼塘全部灌满水.那么不难得出,鱼塘的一半也是满的,转移到铺地砖这个问题上来也是一样的.
以上是关于剑指OFFER 矩形覆盖的主要内容,如果未能解决你的问题,请参考以下文章