Algorithm 以最少的移动次数打开锁
考虑一个由车轮系统组成的锁。每个轮子按顺序有26个字母,每个轮子都用Algorithm 以最少的移动次数打开锁,algorithm,dynamic-programming,Algorithm,Dynamic Programming,考虑一个由车轮系统组成的锁。每个轮子按顺序有26个字母,每个轮子都用'a'初始化。如果向上移动一个轮子,该轮子的显示将移动到字母表的下一个字母;另一方面,向下移动滚轮,将显示切换到字母表的前一个字母。例如: ['a'] -> UP -> ['b'] ['b'] -> DOWN -> ['a'] ... ['z'] -> UP -> ['a'] ['a'] -> DOWN -> ['z'] 只需轻轻一挥,就可以在同一方向上移动任何连续的轮子
'a'
初始化。如果向上移动一个轮子,该轮子的显示将移动到字母表的下一个字母;另一方面,向下移动滚轮,将显示切换到字母表的前一个字母。例如:
['a'] -> UP -> ['b']
['b'] -> DOWN -> ['a']
...
['z'] -> UP -> ['a']
['a'] -> DOWN -> ['z']
只需轻轻一挥,就可以在同一方向上移动任何连续的轮子子序列。这与以这种方式移动子序列的所有轮子的效果相同,只需一次运动。例如,如果目标字符串是'zzzzzzz'
,则将'a'
更改为'z'
的单个移动将使整个车轮序列从'a'
更改为'z'
,从而到达目标字符串——打开锁
如何确定打开锁的最少移动次数?这个问题有动态解决方案吗?该算法必须产生以下结果:
Target string | # moves
______________________________ __________
1 | abcxyz | 5
2 | abcdefghijklmnopqrstuvwxyz | 25
3 | aaaaaaaaa | 0
4 | zzzzzzzzz | 1
5 | zzzzbzzzz | 3
案例1,目标abcxyz
:
aaa[aaa] -> DOWN -> aaazzz
aaa[zz]z -> DOWN -> aaayyz
aaa[y]yz -> DOWN -> aaaxyz
a[aa]xyz -> UP -> abbxyz
ab[b]xyz -> UP -> abcxyz
# = 0 | abcxyz | _DDUUU (choose the smallest subsequence to normalize)
# = 1 | aabxyz | __DUUU (choose the smallest subsequence to normalize)
# = 2 | aaaxyz | ___UUU (choose the smallest subsequence to normalize)
# = 3 | aaayza | ___UU_ (choose the smallest subsequence to normalize)
# = 4 | aaazaa | ___U__ (choose the smallest subsequence to normalize)
# = 5 | aaaaaa | ______ (choose the smallest subsequence to normalize)
案例5,目标zzbzzzz
:
[aaaaaaaaa] -> DOWN -> zzzzzzzzz
zzzz[z]zzzz -> UP -> zzzzazzzz
zzzz[a]zzzz -> UP -> zzzzbzzzz
这个问题可以重新表述为: 将字符串S转换为只包含“a”的字符串的最小移动次数是多少 定义: 将连续子序列视为字符串中相等字符的序列。最小的连续子序列自然是单个字符。如果规范化小的子序列,自然会得到更大的子序列,最终会得到一个子序列——整个字符串 标准化为什么内容: 一个人只能上下移动一个角色,因此,角色本身就是一系列上下移动。字符表示的最坏的情况是字母表中的字母,这至少需要<代码> LEN(字母表)/2 < /CUD>移动来描述。在字母表
{a..z}
中,最坏的情况是'm'
和'n'
因为我们想尽量减少移动的次数,所以我们需要下拉字母C=n
。因此,为了最小化规范化过程,我们必须找到需要相等规范化移动的最大子序列。例如,如果我们有一个目标zzbzzzz
,我们知道最小方向是uuduuu
——U表示向上,D表示向下
规范化:
对于每次移动,计数器都会递增,从而使转换字符串所需的移动次数最少。考虑到上述示例,我们可以采取以下步骤:
# = 0 | zzzzbzzzz | UUUUDUUUU (choose the smallest subsequence to normalize)
# = 1 | zzzzazzzz | UUUUDUUUU (since 'a' is the base character, we choose
the move that increases the largest subsequence;
if 'a' was the first or last character,
moving it would simply be overhead)
# = 2 | zzzzzzzzz | UUUUUUUUU (choose the subsequence to normalize)
# = 3 | aaaaaaaaa | _________ (choose the subsequence to normalize)
另一个示例,使用目标字符串abcxyz
:
aaa[aaa] -> DOWN -> aaazzz
aaa[zz]z -> DOWN -> aaayyz
aaa[y]yz -> DOWN -> aaaxyz
a[aa]xyz -> UP -> abbxyz
ab[b]xyz -> UP -> abcxyz
# = 0 | abcxyz | _DDUUU (choose the smallest subsequence to normalize)
# = 1 | aabxyz | __DUUU (choose the smallest subsequence to normalize)
# = 2 | aaaxyz | ___UUU (choose the smallest subsequence to normalize)
# = 3 | aaayza | ___UU_ (choose the smallest subsequence to normalize)
# = 4 | aaazaa | ___U__ (choose the smallest subsequence to normalize)
# = 5 | aaaaaa | ______ (choose the smallest subsequence to normalize)
编辑:
正如@user1884905所指出的,这个解决方案,正如它所建议的,不是最优的。对于目标字符串mn
,该算法不会产生最佳解决方案:
# = 0 | mn | DU (choose the smallest subsequence to normalize)
# = 1 | ln | DU (choose the smallest subsequence to normalize)
# = 2 | kn | DU (choose the smallest subsequence to normalize)
...
# = 12 | an | _U (choose the smallest subsequence to normalize)
# = 13 | ao | _U (choose the smallest subsequence to normalize)
# = 14 | ap | _U (choose the smallest subsequence to normalize)
...
# = 24 | aa | __ (choose the smallest subsequence to normalize)
这不是最优的,因为以下步骤需要较少的移动:
#0 #1 #2 ... #12
mn -> mm -> ll -> ... -> aa
贪婪算法的最佳子结构可能在于减少字符与字符串之间的全局距离,而不是关注这些字符与基本大小写(
'a'
)之间的差异。因为这只是一些附加信息,也可能是一些优化,这应该是对鲁本斯答案的评论,但在回答中,我可以更好地解释它,它也可以对提问者有用
我还使用了鲁本斯伟大的反向思想
因此,我认为没有必要将a
旋转到其他位置。如果这是正确的(我没有反例),我们就不会把某个东西旋转到错误的方向(我没有数学家的证明,但可能这是正确的)
因此,U
s和D
s的每个子序列将以一个运动每次旋转。此算法不会花费O(n^2)时间。以下是算法:
我们称鲁本斯的字符串为方向字符串
a
的位置旋转目标字符串,并增加计数器(每个子序列一次)a
,最多扫描k/2
后即可完成,其中k
是字母表元素的计数,因此这可能是一个线性时间运行的解决方案
也许有一个解决方案,甚至更少的操作。这只是一个想法,通过寻找递增、递减或“山形”子序列并提取最大值。例如:我们可以说,不需要计算,求解的成本
abcde
- 欧洲央行
abceeddcb
e
编辑:我看到了user1884905的反例。所以我的算法不会找到最优解,但是它可以用来找到正确的算法,所以我还没有删除它
编辑2:另一个适用于示例目标字符串的想法:可以计算出一个平均字母。它是距离目标字符串字母的距离总和最小的字符串。每个字母都应该用上面的算法在这里旋转,然后整个字符串可以旋转到
aaaaaaaa
。由于字母表是循环的,因此可能有多个平均字母(如问题中的第二个示例),在这种情况下,我们应该选择距离a
最小的字母 不清楚你想要什么?你在找算法吗?为什么是三步?你不能做两个动作吗?@Jan