以下代码是一个解决方案。它没有经过彻底的测试,但它完成了问题陈述中给出的示例应该做的事情。
test(new int[]{2 ,2 ,3 ,4, 5, 6, 7, 8 ,9 ,0});
// output: [21, 22, 23, 24, 25, 26, 27, 28, 29, 30], min = 21
test(new int[]{6, 7, 8, 9, 4, 4, 2, 3});
// output: [36, 37, 38, 39, 40, 41, 42, 43], min = 36
它是用 Java 编写的,但转换成另一种语言应该不难。
这是测试方法:
public void test(int[] arr)
{
int min = modifyAndMinimize(arr);
System.out.println(Arrays.toString(arr) + ", min = " + min);
}
数组修饰符:
/**
* Modifies the numbers (which must be non-negative) in arr such that the new version
* is contiguous, meaning that for every subsequence [u, v], v - u = 1.
* The only type of modification that is applied is to insert digits into the elements of arr.
* This can be done in many ways, but this function chooses the way that results in
* the smallest possible first element (which this function returns).
*/
public int modifyAndMinimize(int[] arr) {
for (int i = 0; i < arr.length - 1; i++) {
int u = arr[i], v = arr[i + 1];
int minU = modifyAndMinimize(3, u, v, "", "", 1); // hardcoded 3 out of laziness, but it's really a function of the number of digits of u and v
if (v != minU + 1)
arr[i + 1] = minU+ 1;
if (u != minU) {
arr[i] = minU;
i = -1; // lazy way of moving back to fix the previous numbers
}
}
return arr[0];
}
现在是关键,即“修复”两个连续元素的函数(想法很简单,但实现起来很棘手,而且我昨天发现的深夜无法调试):
/**
* Finds the smallest u' and v' such that v' - u' = diff, where u' and v' are modified versions
* of u and v obtained by inserting digits.
*
* The approach is essentially the common subtraction algorithm, but instead of computing the
* result of v - u, it's given the result and asked to compute u' and v' such that v' - u'
* is the result (diff).
*/
public int modifyAndMinimize(int maxDepth, int u, int v, String doneU, String doneV, int diff) {
if (maxDepth < 0) return Integer.MAX_VALUE;
// if v - u == diff, we're done
if (v - u == diff)
return Integer.parseInt(u + doneU);
// v and/or u need to be modified, but perhaps their last digits are already ok
if ((v - u - diff) % 10 == 0)
return modifyAndMinimize(maxDepth - 1, u / 10, v / 10, u % 10 + doneU, (v % 10) + doneV, diff / 10);
// they're not ok. To fix this, try appending the appropriate digit to u and try appending
// the appropriate digit to v. Choose the option that results in the smallest modified u.
int appendToU = v % 10 - diff;
int carry = appendToU / 10;
appendToU %= 10;
if (appendToU < 0) {
appendToU += 10;
carry--;
}
int option1 = modifyAndMinimize(maxDepth - 1, u, v / 10, appendToU + doneU, (v % 10) + doneV, diff / 10 - carry);
int appendToV = u % 10 + diff;
carry = appendToV / 10;
appendToV %= 10;
if (appendToV < 0) {
appendToV += 10;
carry--;
}
int option2 = modifyAndMinimize(maxDepth - 1, u / 10, v, (u % 10) + doneU, appendToV + doneV, diff / 10 + carry);
return Math.min(option1, option2);
}
很可能有一种更好的方法,而不是使用蛮力来决定是否应该将数字添加到 u 或 v (它在数字数量上是指数的,所以它在数字本身是线性的)。如果是这样,我错过了。