algorithm - 使用不断变化的数组
问题描述
给定一个数组,如果 2 个相邻的数字相等,那么它们可以合并并且它们的值加一。在此过程之后,找到数组中剩余的最小可能元素数。例如:[1,1,1,2,1] -> [1,2,2,1]-> [1,3,1]。因此答案是 3。我尝试使用链表来存储数组,然后遍历整个数组,直到检测到不相等的相邻数字,但这似乎还不够。任何提示或建议都非常受欢迎。感谢您的时间
解决方案
这是一个递归解决方案,但我不知道它是否是最优的:
从数组中最小的数字开始。在示例[1 1 1 2 1]中,为1。原因是合并其他元素后不会得到新的1。所以它们很容易使用。
显然,如果您有偶数个连续的 1,则将它们全部合并绝不是次优的。因此,我们需要决定如何处理奇数个连续元素。其中一个需要省略(不合并),一旦我们选择了那个,其余部分都有偶数个 1。
这里重要的观察是,一旦你选择了要遗漏的元素,它左边的数组和它右边的数组就构成了两个独立的问题。由于中间会有一个 1,因此不能将右侧的任何数字与左侧的任何数字合并。因此,对于每一个可能的选择,您可以递归地解决右子数组和左子数组的问题,然后找到最小结果。
算法
总结该方法,这些是要遵循的步骤:
- 如果数组的长度为 0,则返回 0。
- 找到数组中的最小元素。称它为 x。
- 再检查一次数组,创建一个新数组,其中偶数个连续的 x 都被合并。
- 如果您在数组中的任意位置看到奇数个 x,请执行以下操作:
- 设第一个元素的索引为 i。对于每个属于 x 序列的 j = i, i+2, i+4, ...,解决子数组 [0 .. j-1] 和 [j+1 .. end] 的问题。称他们的结果为 n1 和 n2。
- 从这些可能的拆分中返回最小值 n1 + n2 + 1。
- 如果您没有看到奇数个 x,那么数组中就没有 x。回到步骤 1。
请注意,您可以在第 4 步中将 x 替换为 x+1,并相应地选择子问题索引,以可能在递归调用中节省一些工作。
代码
这是一个执行此操作的 c++ 代码:
#include <iostream>
#include <limits>
#include <vector>
// the range is [start, end)
int
solve(std::vector<int>& array, int start, int end)
{
if (start >= end)
return 0;
int length = end - start;
// find the minimum element
int min = array[start];
for (int i = start; i < end; i++)
if (array[i] < min)
min = array[i];
std::vector<int> newArray;
newArray.reserve(length + 1);
int minCount = 0; // number of consecutive elements that are equal to min
int firstOddNumber =
-1; // index of an odd number of consecutive min's in the new array
int oddNumbers = 0; // number of min's starting at firstOddNumber
for (int i = start; i <= end; i++) {
// iterate one last time with i == end to run the checks again.
// hence the special case. we pop this element after the loop.
int elem = i < end ? array[i] : min + 1;
if (elem == min) {
minCount++;
} else if (minCount != 0) {
// even number of min's
if (minCount % 2 == 0) {
// merge them
for (int j = 0; j < minCount / 2; j++)
newArray.push_back(min + 1);
} else {
// do not merge them but save their index in the new array
firstOddNumber = newArray.size();
oddNumbers = minCount;
for (int j = 0; j < minCount; j++)
newArray.push_back(min);
// ^^^ this part could be modified as I wrote in the note in my
// answer
}
minCount = 0;
newArray.push_back(elem);
} else
newArray.push_back(elem);
}
// remove the min+1 element pushed when i == end
newArray.pop_back();
if (firstOddNumber == -1)
// no odd number of consecutive min's, repeat the procedure
return solve(newArray, 0, newArray.size());
else {
int minResult = newArray.size();
// solve two subproblems for each possible split
for (int i = firstOddNumber; i <= firstOddNumber + oddNumbers; i += 2) {
int result = 1 + solve(newArray, 0, i) +
solve(newArray, i + 1, newArray.size());
if (result < minResult)
minResult = result;
// ^^^ this part could be modified as I wrote in the note in my
// answer
}
return minResult;
}
}
void
test(std::vector<int> v, int expected)
{
int result = solve(v, 0, v.size());
std::cout << result << '\n';
if (result == expected)
std::cout << "CORRECT\n" << std::endl;
else
std::cout << "EXPECTED: " << expected << '\n' << std::endl;
}
int
main()
{
test({ 1, 1, 1, 2, 1 }, 3);
test({ 1, 1, 1, 1, 1 }, 2);
test({ 1, 1, 1, 1, 1, 1 }, 2);
test({ 1, 2, 1, 1, 1 }, 3);
test({ 1, 2, 1, 2, 1 }, 5);
}
推荐阅读
- python - 在 Python 速成课程中获取按钮呈现时遇到问题
- javascript - 使用 jQuery 替换具有命名 id 的两个 div 标签之间的内容(标签和文本)
- python-3.x - 如何在特定子列表之前插入元素?
- javascript - 如何让网页认为它在屏幕上?
- discord.js - 尝试获取 SQLite 值时,discord.js 机器人返回未定义
- r - 在Rstudio中省略大于常数值的值时查找向量的平均值
- java - 在执行 .jar 文件时,`scala` 是否不需要 -jar,而 `java` 需要?
- python - Django 3.0.5 到 sql server 2012 的连接问题
- linux - 两个文件之间的 AWK 部分字符串搜索
- java - 在 Java 中执行 /usr/bin/env bash -c "command"