我正在努力解决一个棘手的问题,我担心我遇到了障碍 - 我没有想法如何解决它。我想也许这里的某个人偶然发现了类似的东西,如果没有,我肯定那些喜欢制作算法的人会喜欢尝试找到解决方案:
我们得到一个未排序的数组。我们被允许进行两个移动之一:从数组中取出任何元素并将其移动到数组的开头或结尾。我们还给出了数组最终应该是什么样子。我们应该用最少的步数对数组进行排序。
示例:
5 1 4 3 2 - > starting array
3 1 2 5 4 - > target array
Steps: move 5 to the end 1 4 3 2 5
move 3 to the beginning 3 1 4 2 5
move 4 to the end 3 1 2 5 4
已达到目标数组,最小步数为3.
有没有人知道如何解决这个问题?
答案 0 :(得分:3)
我相信诀窍是找到两个数组之间最长的公共子序列(你可以在O(n ^ 2)时间内找到它。这将为你提供最大可能的数字组合 don' t 必须移动,相反,必须移动的最小数字集合。一旦你拥有必须移动的数字集合,找出如何移动它们应该是相当简单的。
在你的例子中:
(5,1,4,3,2)和(3,1,2,5,4)之间最长的共同子序列是(1,4),(1,2),(3,2),或(5,4)。每个子序列都告诉你最小移动次数是3(尽管你选择的移动对于每个移动都是不同的,但是看起来很明显。)
修改强>
我认为这基本上是正确的答案(来自Vaughn的一些变化)。
首先,我们像往常一样为最长的公共子序列问题构建我们的子序列长度数组(M [i] [j] =在源[i]和目标[j]结束的最长公共子序列的长度)。
然后,我们不是选择解决方案,而是枚举所有可能最长的公共子序列。
然后我们为每个子序列分配一个分数,该分数是目标序列中最长的连续块的长度。
在示例中,我们得到:
(1,2) - 得分2 (1,4) - 得分1 (3,2) - 得分1 (5,4) - 得分2
我们选择具有最高分数的任何序列并生成适当的移动指令以在此序列之前或之后移动剩余数字。
答案 1 :(得分:0)
如果O(n ^ 2)解决方案足够好,那么算法非常简单。
进行n次迭代,其中n是数组的长度。在迭代i中,找到元素i到n中具有目标排列中最高位置的元素,并将其移动到数组的开头。
迭代1将应该最后的元素移动到开头。迭代2将应该紧挨着的元素移动到它之前。 ...迭代n将最终元素(应该是第一个元素)移动到数组的开头。
答案 2 :(得分:0)
您可以使用A * / IDA *搜索算法执行此操作。 你已经拥有了“开始”和“目标” 启发式函数可以是有序子数组的数量。 新节点的创建可以将元素转换为开始/结束 ......让算法发挥魔力
答案 3 :(得分:0)
如果您遵循@High Performance Mark的建议,您可以取消排名机制。对于您的阵列,重新标记将是(4,2,5,1,3)。在此,LCS是(4,5)和(2,3)。所以你可以直接从这些中的任何一个开始,然后将它们移到中间:
对于4,5:按降序查看小于4的那些并将它们向前移动。然后按升序大于5的那些
对于2,3:按降序查看小于2的那些并将它们向前移动。然后按升序大于3的那些
每个人都有三个动作。
答案 4 :(得分:0)
我同意ArunMK,但他的描述非常缺乏。因此,我建议在C中实现。
#include <stdio.h>
int start[] = { 5, 1, 4, 3, 2 };
int target[] = { 3, 1, 2, 5, 4 };
const int length = sizeof(start) / sizeof(*start);
int canon[length];
void dump_canon()
{
int i;
for (i = 0; i < length; i++)
printf("%d ", target[canon[i]]);
printf("\n");
}
int main()
{
int i, j, k;
/* First "relabel" the array so that the problems becomes: sort the array. */
for (i = 0; i < length; i++) {
for (k = 0; start[i] != target[k]; k++)
/* NO-OP */;
canon[i] = k;
}
printf("Start ");
dump_canon();
/* Search for the longuest ascending sequence without holes */
int longuest_start;
int longuest_length = 0;
for (i = 0; i < length; i++) { /* Use i + longuest_length < length for optimisation */
k = 1;
for (j = i + 1; j < length; j++) { /* condition can be optimized */
if (canon[i] + k == canon[j])
k++;
}
if (k >= longuest_length) {
longuest_start = canon[i];
longuest_length = k;
}
}
/* Now longuest_start has longuest_length ordered values */
/* Increase this ordered values stride by picking a number to put just before or after it */
while (longuest_length < length) {
for (i = 0; i < length; i++) {
if (canon[i] + 1 == longuest_start) {
k = canon[i];
for (j = i; j > 0; j--)
canon[j] = canon[j - 1];
canon[0] = k;
longuest_start--;
longuest_length++;
printf("Take %d and move it to the beginning: ", target[k]);
dump_canon();
}
else if (canon[i] == longuest_start + longuest_length) {
k = canon[i];
for (j = i; j < length - 1; j++)
canon[j] = canon[j + 1];
canon[length - 1] = k;
longuest_length++;
/* XXX We just shifted a new value here, redo this index */
i--;
printf("Take %d and move it to the end: ", target[k]);
dump_canon();
}
}
}
}
输出是:
Start 5 1 4 3 2
Take 5 and move it to the end: 1 4 3 2 5
Take 4 and move it to the end: 1 3 2 5 4
Take 3 and move it to the beginning: 3 1 2 5 4