在一次采访中,我遇到了以下问题:
给出一个大小为n的数组
coin,其中coin[i]表示位于i位置的硬币数量。“移动”包括将一些硬币从i位置移动到i+1位置或i-1位置。重新分配硬币所需的最低移动次数是多少,以便每个位置都有一个硬币?你被保证有相同数量的硬币,因为有槽,以分发他们。
作为一个例子,给定输入
[3, 0, 0, 0, 3, 0, 1]输出是
[1, 1, 1, 1, 1, 1, 1]需要四个步骤:
解决这个问题的有效方法是什么?
发布于 2018-12-08 15:28:43
你只需扫描内部边界,并计数哪些是需要传输的。此计数是最低移动次数。
找到这个最小值的Python代码:
def nb_moves(game):
N=len(game)-1 # number of inside borders.
sum = -1
for i in range(N):
sum += game[i]
if i == sum : N -= 1 # no transfer needed between cells i and i+1 .
return N实际上,让N来计算吧。N显然是移动次数的最小值。此外,可以达到这一最低限度:
N在边框中的总数。这样,每一个内部边界都会被跨越一次,而且只有一次。
一个实现在@templatetypedef的文章中得到了很好的解释。
有些尝试:
In [275]: solve([3, 0, 0, 0, 3, 0, 1])
Out[275]: 4
In [276]: solve([2,2,0,0])
Out[276]: 3
In [277]: solve([1,2,0,1])
Out[277]: 1发布于 2018-12-08 17:25:12
在O(n)时间内,我们可以用一个很好的观察来解决这个问题。诀窍是查看数组的最左边元素。如果你这样做,可能会发生以下三种情况:
[1, 0, 0, 3]将转换为数组[0, 0, 3]。[3, 2, 0, 0, 0]将变成[1, 4, 0, 0, 0],然后我们会删除1以获得[4, 0, 0, 0]。下面是在时间O(N)中运行的上述思想的一个实现:
int moves = 0;
for (int i = 0; i < n; i++) {
/* Case 1 requires no action. */
/* Case 2: leftmost pile has more than one coin. */
if (coins[i] > 1) {
coins[i + 1] += coins[i] - 1;
/* No need to edit coins[i]; we won't touch it again. */
}
/* Case 3: Find a pile and shift it back. */
else if (coins[i] == 0) {
/* Total up coins until a free spot is found. */
int k = 1;
int zeroes = 1;
int cumTotal = coins[i + k];
while (cumTotal <= k) {
k++;
if (coins[k] == 0) zeroes++;
cumTotal += coins[i + k];
}
/* Remove from that pile enough coins to cover all the zeroes encountered. */
coins[k] -= zeroes;
moves += k;
/* Continue our scan after this position. */
i = k;
}
}
return moves;发布于 2018-12-08 08:06:52
一次只能移动一枚硬币:
这个问题的一个简单解决方案是迭代所有位置和每个有0硬币的位置,从左到右迭代找到一个有多个硬币的位置,并保持跟踪总移动要求将硬币从该位置带到初始位置。
int minimumCoinMoves(vector<int>&coins) {
int n = coins.size();
int moves = 0;
for (int i = 0; i < n; ++i) {
if (coins[i] == 0) {
for (int j = 0; j < n; ++j) {
if (coins[j] > 1) { // fill up ith place with coin in jth place
coins[i] = 1;
coins[j]--;
moves += abs(j - i); // total moves from jth to ith place
break;
}
}
}
}
return moves;
}如果我们假设我们有n硬币,那么这种方法将花费O(n * n)的时间复杂性。在n值很大的情况下,这种复杂性可能不可行。我们可以通过简单地修改上面的解决方案来跟踪其他向量中的额外硬币的位置,并从所有位置从0硬币的位置从左到右检索硬币,从而降低我们的复杂性。
int minimumCoinMoves(vector<int>&coins) {
int n = coins.size();
int moves = 0;
vector<int>extraCoinIndices;
for (int i = 0; i < n; ++i) {
if (coins[i] > 1) {
extraCoinIndices.push_back(i);
}
}
int ptr = 0;
for (int i = 0; i < n; ++i) {
if (coins[i] == 0) {
moves += abs(extraCoinIndices[ptr] - i);
coins[i] = 1;
coins[ extraCoinIndices[ptr] ]--;
if (coins[ extraCoinIndices[ptr] ] == 1) ptr++;
}
}
return moves;
}编辑:
一次可以移动任意数量的硬币:
如果我们认为任何数量的硬币可以在移动中从一个位置移动,我们可以在O(n)中做以下更改来解决它。
int minimumCoinWithAnyNumberMoves(vector<int>&coins) {
int n = coins.size();
int moves = 0;
int farZero = -1;
int extraCoins = 0;
for (int i = 0; i < n; ++i) {
if (coins[i] == 0) {
if (extraCoins > 0) {
extraCoins--;
moves++;
continue;
}
farZero = (farZero == -1) ? i : farZero;
} else {
if (farZero == -1) {
if (extraCoins > 0) moves++;
extraCoins += (coins[i] - 1);
continue;
}
assert(extraCoins == 0);
int totZeros = i - farZero;
moves += totZeros;
if (coins[i] <= totZeros) {
farZero = farZero + coins[i];
} else {
farZero = -1;
extraCoins += (coins[i] - totZeros - 1);
}
}
}
return moves;
}https://stackoverflow.com/questions/53680197
复制相似问题