给出数字列表
1 15 2 5 10
我需要获得
1 2 5 10 15
我能做的唯一操作是“在位置Y移动数字X”。
在上面的示例中,我只需要“在第5位移动数字15”。
我想尽量减少操作次数,但鉴于可用的操作,我无法找到/记住经典算法。
一些背景知识:
答案 0 :(得分:2)
引理:您可以执行的对列表 L (按递增顺序)排序的最小(删除元素,插入元素)对的数量为:
S min (L) = | L | - | LIC(L) |
LIC(L)是最长增长子序列。
因此,你必须:
<强>证明:强> 通过归纳。
对于大小为1的列表,增长最长的子序列的长度为1!该列表已经排序,因此所需的(del,ins)对的数量为
| → | - | LIC(L) | = 1 - 1 = 0
现在让 L n 成为长度为 n 的列表,1≤ n 。设 L n + 1 是通过在<的左边添加元素 e n + 1 获得的列表EM>→<子>名词子> 的。
此元素可能会或可能不会影响最长增加子序列。让我们试着看看......
让 i n,1 和 i n,2 成为的两个第一个元素LIC(L n ) (*):
在第一种情况下,我们删除 e n + 1 ,因此我们得到排序 L n 。通过归纳假设,这需要 n (删除,插入)对。然后,我们必须在适当的位置插入 e n + 1 。因此:
S(L n + 1 ) min = 1 + S(L n ) <子>分钟子> 的 S(L n + 1 ) min = 1 + n - | LIC(L 名词子>) | S(L n + 1 ) min = | L n + 1 | - | LIC(L n + 1 |
在第二种情况下,我们忽略 e n + 1 。我们首先删除不在 LIC(L n )中的元素。必须再次插入这些元素!有
S(L n ) min = | L n | - | LIC(L n ) |
这样的元素。
现在,我们必须小心并按正确的顺序插入它们(相对于 e n + 1 )。最后,它需要:
S(L n + 1 ) min = | L n | - | LIC(L n ) | S(L n + 1 ) min = | L n | + 1 - (| LIC(L n ) | + 1)
因为我们有| LIC(L n + 1 ) | = | LIC(L n ) | + 1和| L n + 1 | = | L n | + 1,我们最终:
S(L n + 1 ) min = | L n + 1 | - | LIC(L n + 1 ) |
通过考虑通过删除 i n,1 <获得的列表 L&#39; n ,可以证明最后一种情况/ em>来自 L n + 1 。在那种情况下, LIC(L&#39; n ) = LIC(L n + 1 )因此:
| LIC(L&#39; <子>名词子>) | = | LIC(L n ) |的(1)强>
从那里,我们可以对 L&#39; n 进行排序(这需要| L&#39; n | - | LIC(L&#39; n |由归纳假设。先前的等式(1)导致结果。< / p>
(*):如果 LIC(L n )&lt; 2,然后 i n,2 不存在。只需忽略与它的比较。在这种情况下,只有案例2和案例3适用......结果仍然有效
答案 1 :(得分:0)
一种可能的解决方案是找到longest increasing subsequence并仅移动不在其中的元素。
我无法证明它是最优的,但很容易证明它是正确的并且比N交换更好。
这是Python 2中的概念验证。我将其实现为O(n 2 )算法,但我很确定它可以简化为O(n log n) )。
from operator import itemgetter
def LIS(V):
T = [1]*(len(V))
P = [-1]*(len(V))
for i, v in enumerate(V):
for j in xrange(i-1, -1, -1):
if T[j]+1 > T[i] and V[j] <= V[i]:
T[i] = T[j] + 1
P[i] = j
i, _ = max(enumerate(T), key=itemgetter(1))
while i != -1:
yield i
i = P[i]
def complement(L, n):
for a, b in zip(L, L[1:]+[n]):
for i in range(a+1, b):
yield i
def find_moves(V):
n = len(V)
L = list(LIS(V))[::-1]
SV = sorted(range(n), key=lambda i:V[i])
moves = [(x, SV.index(x)) for x in complement(L, n)]
while len(moves):
a, b = moves.pop()
yield a, b
moves = [(x-(x>a)+(x>b), y) for x, y in moves]
def make_and_print_moves(V):
print 'Initial array:', V
for a, b in find_moves(V):
x = V.pop(a)
V.insert(b, x)
print 'Move {} to {}. Result: {}'.format(a, b, V)
print '***'
make_and_print_moves([1, 15, 2, 5, 10])
make_and_print_moves([4, 3, 2, 1])
make_and_print_moves([1, 2, 4, 3])
输出如下内容:
Initial array: [1, 15, 2, 5, 10]
Move 1 to 4. Result: [1, 2, 5, 10, 15]
***
Initial array: [4, 3, 2, 1]
Move 3 to 0. Result: [1, 4, 3, 2]
Move 3 to 1. Result: [1, 2, 4, 3]
Move 3 to 2. Result: [1, 2, 3, 4]
***
Initial array: [1, 2, 4, 3]
Move 3 to 2. Result: [1, 2, 3, 4]
***