经典动态规划:四键键盘 :: labuladong的算法小抄 #1052
Replies: 21 comments 12 replies
-
我就是CV程序员了XD |
Beta Was this translation helpful? Give feedback.
-
这个递推公式跟别的递归都不一样,每一步都重新计算前面所有可能,复杂度O(n)的状态转移第一次见,算是奇葩题吧。。 |
Beta Was this translation helpful? Give feedback.
-
很巧妙的第二种解法 |
Beta Was this translation helpful? Give feedback.
-
为什么是C-A,C-C之后一直C-V呢,如果再一定数量的时候,再拷贝一次,底数应该就翻倍了,效果后续肯定能超过一直C-V,如果在txt里面凑字数的话,肯定能感受出来的。 |
Beta Was this translation helpful? Give feedback.
-
一旦我们C-A,C-C了一次之后,就没有按下A的必要了,因为C-V肯定比A多,我们考虑的肯定是什么时候,采用C-A,C-C增倍 |
Beta Was this translation helpful? Give feedback.
-
请教一下,第二种解法的状态定义是不是有一些问题?如果dp[i]定义为剩下为i个操作数,屏幕上A的数量。那最后的结果应该是dp[0]才对吧 |
Beta Was this translation helpful? Give feedback.
-
我想知道图中 j = 5 的地方是 3 还是 6,j=5的地方是C-V操作,应该是6,推到后面就不对了 |
Beta Was this translation helpful? Give feedback.
-
我的理解是 当剩余操作数n<=6的时候 直接打A的数量是最优的,因为先打3个A然后CACCCV一套也是6步产生6个A,在之后的话就要考虑继续CV还是重来一遍CACCCV;当cv粘贴的数量<CACCCV重新复制粘贴的数量-3时 可以选择后者 |
Beta Was this translation helpful? Give feedback.
-
确实看懂了 也能自己写出来,但是这种思路真的很巧妙,感觉没做过的话面试应该在有限时间内想不出来的。 感觉j可以从3开始一直到i,毕竟如果j = 2实际就是屏幕上没有A就ctrlA ctrlC,相当于什么也没复制 |
Beta Was this translation helpful? Give feedback.
-
我感觉这题应该有一种数学办法可以直接算出来。 |
Beta Was this translation helpful? Give feedback.
-
for (int j = 2; j < i; j++) 个人感觉这里j可以等于i吧,就相当于第i次操作可以复制,相应的dp[i] = Math.max(dp[i], dp[j - 2] * (i - j + 2)); 不然的话,第j次操作是复制,为啥不能等于i?当前第i次操作就只能是按A了,虽然i+1的时候j还会遍历到当前的i,但是最后i=N的时候j最大N-1,那最后一次操作不就只能按A了么?这里也没讲明白啊 |
Beta Was this translation helpful? Give feedback.
-
我自己dubug悟了,“所以我们用一个变量 j 作为若干 C-V 的起点。那么 j 之前的 2 个操作就应该是 C-A C-C 了”这里表述是有问题的,j那次操作不是CV而是CC,j+1才是CV的起点,j之前也就是j-1那一次的操作是CA,j-2是什么操作不知道,反正dp[j-2]是j-2次操作下的最优值,因为j-1是CA,j是CC,所以i-j就是j之后包括i在内的粘贴次数,是对dp[j-2]的粘贴,再加上dp[j-2]本身,所以dp[i]可以为dp[j - 2] * (i - j + 1)。另外最后一个图画的也不准确,根据定义:dp[i] 表示 i 次操作后最多能显示多少个 A,那么图中下标为4的地方就是第四次操作也就是CA,所以直接把CA画在下标4那里就可以。以上是个人理解,如有错误,欢迎指出。 |
Beta Was this translation helpful? Give feedback.
-
哦另外,j是CC,i是CV,所以“for (int j = 2; j < i; j++)” 这里j<i是解释的通的,(i - j + 1)已经把i是粘贴算进去了。 |
Beta Was this translation helpful? Give feedback.
-
打卡打卡 dp[i] = Math.max(dp[i], dp[j - 2] * (i - j + 1)); |
Beta Was this translation helpful? Give feedback.
-
关于简化Ctrl+V的次数
|
Beta Was this translation helpful? Give feedback.
-
对这一段的理解: 最优的结果,可以是 C-A C-C C-V C-V C-V C-V C-V C-V 也可以是 C-A C-C C-V C-V C-A C-C C-V C-V 一开始觉得后者可能是更优的结果,感觉作者的说法有问题。 后来想明白,这两种情况,都在这个 |
Beta Was this translation helpful? Give feedback.
-
我在没看第二种方法之前自己改了一种第一种笨办法的剪枝,目前可以通过所有case,用原来的做选择去思考,比较intuitive def maxA(self, n: int) -> int:
# 比较容易推导在n为7之前最效率的方法就是按6次A或者3次A然后三键套
# 得出的结果均为6
if n <= 6:
return n
memo = {}
# dp的param现在是一个会放进memo的key
def dp(key):
if key in memo:
return memo[key]
n, a_num, copy = key[0], key[1], key[2]
# base case
if n <= 0:
memo[key] = a_num
return a_num
# 如果屏幕上的A和cache里的一样多
# 说明我们才进行了 C-A, C-C, 下一步必定是C-V, 其实只有一种选择
if a_num == copy:
res = dp((n - 1, a_num + copy, copy))
memo[(n - 1, a_num + copy, copy)] = res
return res
# 比较我们是继续C-V 还是重新C-A, C-C 收益高
else:
res_paste = dp((n - 1, a_num + copy, copy)) # C-V
res_copy = dp((n - 2, a_num, a_num)) # C-A C-C
if res_paste > res_copy:
memo[(n - 1, a_num + copy, copy)] = res_paste
return res_paste
else:
memo[(n - 2, a_num, a_num)] = res_copy
return res_copy
# dp的调用,比较(n - 6), 即6键之后的两种最优情况优劣
# 第一种是6个A上屏,cache里存了3个A (3次A然后三键套)
# 第二种是4个A上屏,cache里存了4个A (4次A然后copy所有A)
return max(dp((n - 6, 6, 3)), dp((n - 6, 4, 4))) |
Beta Was this translation helpful? Give feedback.
-
打卡,附上详细注解的Java代码,希望有个老哥可以帮我测一下,我没有plus会员 public int maxA(int N){
//dp数组的定义,当你进行到第i次操作的时候你屏幕中最多有dp[i]个A,这里什么是N+1呢,因为整体右移一位,i=0无意义
int[] dp=new int[N+1];
//i=0的时候无意义,所以先赋一个初值
dp[0]=0;
//base case:第一次操作的时候,屏幕中A要是最多的话,只能是输入了一个A。
dp[1]=1;
//做选择,我们的选择要保证屏幕中A最多,所以有两种情况
//1.我们一直按A
//2.我们先按A,然后在合适的地方按C-A,C-C,然后一直按C-V,如果是这种情况的话,我们要记录第一次按C-V的位置
for (int i =2 ; i <=N ; i++) {
dp[i]=dp[i-1]+1;//如果我们选择按A的话,就是前面的屏幕中A的个数然后加1即可
for (int j = 2; j <i ; j++) {
//选择第二种方案的话,我们用j来记录第一次按C-V的地方,我们屏幕中的最多A的数量就是
// 从j到i一直按C-V,那么dp[j]的时候的屏幕上的A的个数乘以(i-j+1)就是屏幕中最大的A的数量
dp[i]=Math.max(dp[i],dp[i-2]*(i-j+1));
}
}
//最后返回的结果就是全部按完的时候的结果
return dp[N-1];
} |
Beta Was this translation helpful? Give feedback.
-
内部循环这样会不会更好呢 } |
Beta Was this translation helpful? Give feedback.
-
我花了好久才意识到,最优的方法并不是先打一些A,然后一直Ctrl-V到结束!例如,给定11步,最好的方法是 a, a, a, Ctrl A + C, Ctrl V, Ctrl V, Ctrl A + C, Ctrl V, Ctrl V。 我画了一张图来解释(英文 Leetcode):https://leetcode.com/problems/4-keys-keyboard/solutions/3737545/the-optimal-way-is-not-typing-a-s-at-first-and-then-ctrl-v-to-the-end-with-drawing/ |
Beta Was this translation helpful? Give feedback.
-
文章链接点这里:经典动态规划:四键键盘
评论礼仪 见这里,违者直接拉黑。
Beta Was this translation helpful? Give feedback.
All reactions