动态编程-4键键盘

时间:2018-12-27 23:47:51

标签: python algorithm dynamic-programming

  

想象一下,您有一个特殊的键盘,它具有以下按键:

     

键1:(A):在屏幕上打印一个“ A”。

     

键2:(Ctrl-A):选择整个屏幕。

     

键3:(Ctrl-C):将所选内容复制到缓冲区。

     

键4:(Ctrl-V):在屏幕上打印缓冲区,将缓冲区追加到   已被打印。

     

现在,您只能按键盘N次(对于上述四次   键),找出可以在屏幕上打印的最大A数。

示例1:

输入:N = 3

输出:3

说明:按以下键序列,我们最多可以在屏幕上获得3 A的字样:A, A, A

示例2:
输入:N = 7

输出:9

说明:按以下键序列,我们最多可以在屏幕上获得9 A:A, A, A, Ctrl A, Ctrl C, Ctrl V, Ctrl V

(免责声明:我不想听到其他解决方案。我只是想了解我所缺少的内容以及解决方法。)

这是我当前的(错误的)解决方案(下面的解释):

class Solution:
    def maxA(self, N):
        screen = [0] * N
        screen[0] = 1
        applied_clipboard = clipboard = select = 0        
        for i in range(1, N):
            if i < 3:
                screen[i] = screen[i-1] + 1
            else:
                screen[i] = max(screen[i-3] + clipboard, screen[i-1] + 1, screen[i-1] + applied_clipboard)

                if screen[i] == screen[i-3] + clipboard:
                    applied_clipboard = clipboard

            select, clipboard = max(select, screen[i-1]), max(clipboard, select)
        return screen[-1]

我正在跟踪一些状态,如上面的代码所示。状态是:

  1. 我选择了多少个字符?
  2. 我在屏幕上打印了多少个字符?
  3. 剪贴板中有多少个字符?
  4. 我实际应用的最新剪贴板是什么?

有了这些状态,我相信我可以在每个步骤中做出最佳决策。

但是,我的代码不正确。对于N = 11的输入,我的代码返回27,正确的值为27。但是,对于N=9N=10,正确的值分别是1620,但是我分别得到1518 )。

有人以我更新状态的方式看到该错误吗?

编辑。回应现有答案: 我了解我的状态更新可能不完整,但问题是在哪里。我的想法是使每个状态都最大化,因为有一系列单独的决策来使每个状态最大化。然后,在接下来的迭代中使用所有这些信息来更新状态。

重写我的代码以保留每个片段的状态。

class Solution:
    def maxA(self, N):
        screen = [0] * N
        screen[0] = 1
        applied_clipboard, clipboard, select = [[0] * N for _ in range(3)]

        for i in range(1, N):
            if i < 3:
                screen[i] = screen[i-1] + 1
            else:
                screen[i] = max(screen[i-3] + clipboard[i-1], screen[i-1] + 1, screen[i-1] + applied_clipboard[i-1])

                if screen[i] == screen[i-3] + clipboard[i-1]:
                    applied_clipboard[i] = clipboard[i-1]
                else:
                    applied_clipboard[i] = applied_clipboard[i-1]

            select[i] = max(select[i-1], screen[i-1])
            clipboard[i] = max(clipboard[i-1], select[i-1])

        print('screen', screen)
        print('clipboard', clipboard)
        print('applied', applied_clipboard)
        return screen[-1]

我想让我感到困惑的是,如何保留已应用的剪贴板值(applied_clipboard)与剪贴板的最大状态。但是我需要区分两者,因为这会影响递归关系。

感谢user3386109使我看到了这一点:

  • N=9采取的一系列正确步骤是AAAASCVVV(总共16个A)
  • 我的代码N=9输出AAASCVVVV(总共15个A)。

其中s =选择,c =复制,v =粘贴。

3 个答案:

答案 0 :(得分:2)

错误很简单,就是代码在每个步骤都最大化了屏幕上'A'的数量。如果print screen为9时在函数的末尾N,您将看到以下信息:

[1, 2, 3, 4, 5, 6, 9, 12, 15]

请注意,当N为6时,屏幕上“ A”的最大数量为6。这可以通过三种不同的方式实现:

AAAAAA
AAscvv
AAAscv

其中s =选择,c =复制,v =粘贴。

但是,当N为9时,正确答案为

AAAAscvvv

如您所见,按下6键后,屏幕上的“ A”数仅为4,而不是6。因此,在每一步中最大化屏幕上的“ A”数并不能给出正确的答案。

答案 1 :(得分:2)

一种思考的方法是,我们的解决方案必须以paste结尾,因为显然我们不想以selectcopy结尾来浪费印刷机。然后,我们可以假设我们要选择粘贴的屏幕状态是最佳的(否则,为什么要从中粘贴?)。

f(n)代表可以输出的A的最大数量。然后,我们可以选择在3次或更多次按后重复应用任何屏幕状态。例如,在第七次按时,我们可以回头说:要重复屏幕状态4(仅为4),我们需要按3次,因此从4重复将提供我们的记录,即7、4 + 4 = 8 {{1} } s。如果我们尝试从3开始重复,则需要3次按压才能重复1次,使我们达到6次,我们仍然可以再按1次,所以3 + 2 * 3 =9。因此,重复应用屏幕状态3将提供记录连续7九个A,这是最好的。对于任何A,我们可以获得第0 < j < i-3个屏幕状态的1 + i - (j + 3) + 1i - j - 1倍。

JavaScript代码:

j

检查数据,我发现要复制的最佳屏幕状态似乎始终非常接近function f(n) {   const m = [0,1,2,3,4,5,6].concat( new Array(Math.max(n-6,0)).fill(0));   for (let i=7; i<=n; i++)    for (let j=1; j<=i-3; j++)      m[i] = Math.max(m[i], m[j]*(i-j-1)); console.log(JSON.stringify(m)); return m[n]; } console.log(4, f(4)); console.log(7, f(7)); console.log(9, f(9)); console.log(10, f(10)); console.log(11, f(11));(向后6个状态),并且向后遍历时似乎始终是凸面的,这意味着我们可以通过向后遍历来进行优化并寻找向下的变化。

如果这种行为是一致的,则意味着该算法具有n复杂性。

O(n)

答案 2 :(得分:1)

据我所知(现在,我已经更改了代码以使其可运行),您正在进行更新。我怀疑问题在于状态更新不完整。不幸的是,我认为您的实现是正确的,但是您的算法是错误的。

认为,您的问题是每次迭代screen都会使您的长期目标短路。在每次迭代中,您都将处理选择,就好像此按键必须是您的最后一次。结果,您有时会放弃潜力更大的剪贴板,而只用最能使您A来完成此步骤的更新。

您需要为每个步骤维护多个有用的状态:不仅是最大的屏幕显示,还包括丰富的剪贴板的潜力。

更新 我认为我们现在在一起思考。您需要为每种击键次数存储多个状态(部分解决方案)。这不是简单的递归关系:它更多是量子状态。

例如,在7次击键时,您需要表示至少两个状态。

  • 基于AAAASCV,屏幕上有8个A,剪贴板中有4个。 (8,4)
  • 由于AAASCVV,屏幕上有9个A,剪贴板中有3个。 (9,3)

您当前的算法在每次击键次数中分别只为screenclipboard提供一个值。它仅保留具有最佳短期结果的解决方案。它错过了(8,4)是9和10次击键的更好垫脚石。

您需要保留每个步骤中不占优势的所有对。当G(screen) >= H(screen) G(clipboard) >= H(clipboard)时,状态G“支配”状态H。


还要注意,您需要在状态中使用较少的元素:当前选择的元素。例如,考虑上述两种状态的情况(为便于说明),进一步沿中风(粘贴),以及“提前计划”的另一种状态:

scr   clip   sel
12      4     0          Nothing selected
12      3     0          Nothing selected
 9      3     9          Stopped at 9 A's and did a select-copy

第三个状态将立即超过前两个状态,又走了两个步骤。