优化DP解决方案的空间复杂性

时间:2015-12-11 00:12:00

标签: c++ algorithm dynamic-programming memoization

问题

  

我有一张10像素(1-D阵列)的照片(黑白)   字符'b'和'w',b表示黑色,w表示白色。)   也有N个过滤器。每个滤波器都是一个由D组成的阵列(大小为10)   '+'和' - '。您可以选择这些N个过滤器的任何子集并应用   他们在照片上。
应用过滤器:如果 i th   过滤器的字符是'+',反转 i th 像素   照片其他没有任何反应。
我们需要输出的数量   不同的过滤器子集我们可以选择将照片转换为   全黑。

约束

  

1 <= T(测试用例数)&lt; = 5
1&lt; = N(Number of Number   滤波器)&LT; = 10 5

我的方法

  

基本的想法是我有一个功能NumberPossible(i,   startingPosition),其中i是尚未使用的过滤器的索引。   
我从0到n不等。
基础案例:NumberPossible(n,   当且仅当startingPosition是 all时,startingPosition)等于1   黑。否则,0。复发:NumberPossible(i,   startingPosition)= NumberPossible(i + 1,applyFilter(i,   startingPosition))+ NumberPossible(i + 1,startingPosition)那个   表示如何应用过滤器或不应用过滤器   过滤器。

问题

  

以下方法超时(可能是由于昂贵的步骤   在代码中提到)。有没有更好的算法,我在哪里   不需要存储所有状态,并且需要更少的空间   复杂性?

1 个答案:

答案 0 :(得分:1)

DP的概念是,您可以通过所需的所有中间信息递归地从最终需要的信息向后工作,一直回到您可以直接计算的内容。同时,您可以避免重新计算通过递归在多个路径中所需的任何中间值。

但DP的典型现实是,知道哪些中间值已经计算的成本使得实际工作在相反方向上更便宜。从您可以直接计算的内容开始。无论你是否知道你需要它们,都要计算所有这些。然后步入可以从那些计算的事物并计算所有这些,再次不知道您是否需要它们。在典型的情况下,来自&#34;是否最终需要它的多余计算&#34;与以有效序列计算事物的好处相比,这是微不足道的。

存在语义问题(我已经看到积极维护的两个方面)第二种方法是否真的是DP,或者DP是否只是第一种方法,而假设的DP解决方案是用来帮助定义非DP解决方案。

你的问题中的方向在这个讨论中是一个令人困惑的因素,因为任何一种方法都允许可逆方向,但两种方法之间的关键区别在于方向相反。真正发生的是与相对方向相反的存储答案的含义。

因此,我们假设我们已将j计算为0或1,并将dp[j][Y] 计算为所有Y ,以表示考虑所有输入之前的组合 i代表某些i以及ij之间的某种关系(所有这些都发生在i的循环中)。

接下来,我们可以循环Y以计算所有Y的所有Y的dp[1-j][Y],并考虑输入i之前的所有输入。

之后,我们将j1-j交换(只需编码j=1-j;)或从j的下一个值重新计算i

我们可能希望检测并跳过输入中的某些空位置,这成为与j交换1-j的原因可能更好地作为递增i的独立操作。但这是一个相当先进的优化。

由于您知道如何从dp[i+1][y]dp[i][]和输入y计算i,因此您知道如何将所有内容放入i的循环中和y上的内部循环,但改为使用dp[j][]dp[1-j][]

接下来,更大的优化是计算输入。由于输入序列无关紧要,我们可以完全跳过filters[i]=x并使用filters

,而不是存储filter_count[x]++

然后,主i循环覆盖filter_count的1024个元素,而不是filter的N个元素。

sometype global_multiplier=1;
for (unsigned i=0; i<1024; ++i)
if (filter_count[i]) {  // only do the work if we have any
    // Multiply by half the number of subsets of this filter
    for (unsigned c=filter_count[i]; --c;)
        global_multiplier=(global_multiplier*2)%MOD;

    for (unsigned y=0; y<1024; ++y)
        dp[1-j][y] = (dp[j][y] + dp[j][y^i]) % MOD;
    j=1-j;
    }

最后选择dp[j][]的正确位置,并将其乘以单独保留的global_multiplier

对于该单个最终乘法,您需要两倍的数字,因为MOD中有数字。但是在其他任何地方,你需要的值只比MOD中的位多一点。如果真的被迫,最后的乘法可以通过shift和add来完成,所以它也只需要比MOD本身需要多一点。