为什么这个问题的时间复杂度只考虑了前面的递归调用,而不考虑整个问题?

Why is the time complexity of this problem only consider the previous recursive call and not the entire problem?

提问人:herophant 提问时间:7/13/2019 更新时间:7/13/2019 访问量:68

问:

在这里,我们有一个 4 * 7 的框,它可以填充 1 * 2 或 2 * 1 的矩形。这段描述出自《竞争程序员手册》一书。

enter image description here

为了最有效地解决这个问题,书中提到使用可以在特定行中的部分:

enter image description here

由于该集合中有 4 个内容,因此我们可以拥有的最大唯一行数为 4^m,其中 m 是列数。从每个构造的行中,我们构造下一行,使其有效。有效意味着我们不能让垂直片段乱序排列。只有当顶行中的所有垂直“大写字母”都与底行中的垂直“杯子”相对应时,反之亦然,解决方案才有效。(显然,对于水平片段,它们的构造在行创建本身中受到限制,因此此处不可能存在行间差异。

然后,这本书是这样说的:

由于一行由 m 个字符组成,并且有四个选项 每个字符,不同行数最多为 4^m。因此, 解的时间复杂度为 O(n4^{2m}),因为我们可以通过 每行的 O(4^m) 可能状态,对于每个状态,有 O(4^m) 上一行的可能状态。

一切都很好,直到最后一句话,“前一行有 O(4^m) 种可能的状态。为什么我们只考虑前一行?有更多的行,这一次复杂度应该考虑整个问题,而不仅仅是前一行,对吧?

这是我对 n 矩阵 2 的临时 C++ 实现,在这种情况下不起作用,但我试图抽象它:

int ways[251];

int f(int n){
    if (ways[n] != 1) return ways[n];
    return (ways[n] = f(n-1) + f(n-2));
}

int main(){
    ways[0] = 1;
    ways[1] = 1;
    for (int i = 2; i <= 250; i++){
        ways[i] = -1;
        cout << f(250) << '\n';
    }
}
C++ 算法 与语言无关的 动态规划

评论

0赞 J. S. 7/13/2019
因为我们假设除了最后两行之外的前一行已经完全填充(这称为使用 profile 进行动态编程,其中 profile 是最后两行的掩码)
0赞 herophant 7/13/2019
@J.S.因此,对于每一行,我们只考虑前一行。也就是说,如果我们有 4 行,我们想弄清楚有多少种方法可以填充第 3 行,我们比较第 2 行的某个固定状态,看看我们可以填充第 3 行有多少种方法。但是第二排的其他状态呢?我们也必须单独计算这些,对吧?
0赞 J. S. 7/13/2019
主要思想是计算每个掩码的可能状态数。因此,当您填充第三行时,对于每个蒙版,您知道可以到达它的方式(包括如何填充第一行和第二行),然后放置新的矩形并将此值添加到新蒙版中。这就是你获得新价值的方式。当您完全填充第三行时,您不再对包含它的蒙版感兴趣,但您知道第四行所有蒙版的值,并将第五行添加到所有蒙版中。
0赞 herophant 7/14/2019
@J.S. 好的。所以 O(n) 来自我们这样做 n 次(行数)的事实。第一个 O(4^m) 来自每行最多有 4^m 的可能性,因此我们假设所有可能性。第二个 4^m 来自前一行,您必须再次完成所有安排。1) 这是正确的吗?2) 什么是口罩?只是一行的状态,比如它们可以拥有什么?3) 第一排做什么?你不能在这里使用上一行,对吧?感谢您到目前为止的帮助。随意在答案中包含这些东西。
0赞 J. S. 7/14/2019
1) 也许我不明白你的意思,但从中我们必须遍历填充第 2 行和第 2 行的所有 2^n 种可能性,是的,我们应该遍历所有这些。2) 是的。3)对于您拥有的第一行,我们可以假设它是我们从上一行中得到的(0表示第一行单元格都没有被占用)。O(4^n)k-1kd[0] = 1

答: 暂无答案