
2026-04-26:使循环数组余额非负的最少移动次数。用go语言,给定一个环形排列的数组 balance,长度为 n,其中 balance[i] 表示第 i 个人当前的净余额(正数代表有剩余,负数代表欠债)。
在一次操作中,你可以选择某个人,把恰好 1 单位余额转给他的左邻居或右邻居(因为是环形,首尾相邻)。
目标:通过若干次这样的转移,使得所有位置的余额都变为非负(即每个人都不再欠债)。
要求:输出实现该目标的最小操作次数;如果从初始状态出发无法做到,则输出 -1。
已知条件:初始时数组中最多只有一个位置的余额为负。
1 <= n == balance.length <= 100000。
-1000000000 <= balance[i] <= 1000000000。
balance 中初始至多有一个负值。
输入:balance = [1,2,-5,2]。
输出:6。
解释:
一种最优的移动序列如下:
从 i = 1 移动 1 个单位到 i = 2,结果 balance = [1, 1, -4, 2]
从 i = 1 移动 1 个单位到 i = 2,结果 balance = [1, 0, -3, 2]
从 i = 3 移动 1 个单位到 i = 2,结果 balance = [1, 0, -2, 1]
从 i = 3 移动 1 个单位到 i = 2,结果 balance = [1, 0, -1, 0]
从 i = 0 移动 1 个单位到 i = 1,结果 balance = [0, 1, -1, 0]
从 i = 1 移动 1 个单位到 i = 2,结果 balance = [0, 0, 0, 0]
因此,所需的最小移动次数是 6。
题目来自力扣3776。
negIdx=2负数位置是索引2,余额为-5,需要补充5单位余额才能变成0(非负),记need=5(需要的总余额数)。
初始化总操作次数ans=0。
因为是环形数组,我们从离负数位置最近的地方开始收集余额(距离越近,移动步数越少,符合最小操作次数要求),距离从1开始依次递增:
s=2+2=4s=1总操作次数为6,与题目示例输出一致。
n次操作(n是数组长度);n,可以视为常数次;n成正比 → 时间复杂度为 O(n)。total、negIdx、need、ans、dis、s等常数个变量;n相关的额外数组、集合等数据结构;.
package main
import (
"fmt"
)
func minMoves(balance []int)int64 {
total := 0
negIdx := -1
for i, x := range balance {
total += x
if x < 0 {
negIdx = i
}
}
if total < 0 { // 总和必须非负
return-1
}
if negIdx < 0 { // 没有负数,无需操作
return0
}
n := len(balance)
need := -balance[negIdx]
ans := 0
for dis := 1; ; dis++ { // 把与 negIdx 相距 dis 的数移到 negIdx
s := balance[(negIdx-dis+n)%n] + balance[(negIdx+dis)%n]
if s >= need {
ans += need * dis // need 个 1 移动 dis 次
returnint64(ans)
}
ans += s * dis // s 个 1 移动 dis 次
need -= s
}
}
func main() {
balance := []int{1, 2, -5, 2}
result := minMoves(balance)
fmt.Println(result)
}

.
# -*-coding:utf-8-*-
from typing import List
def minMoves(balance: List[int]) -> int:
total = 0
neg_idx = -1
for i, x in enumerate(balance):
total += x
if x < 0:
neg_idx = i
if total < 0: # 总和必须非负
return-1
if neg_idx < 0: # 没有负数,无需操作
return0
n = len(balance)
need = -balance[neg_idx]
ans = 0
dis = 1
while True: # 把与 neg_idx 相距 dis 的数移到 neg_idx
left = balance[(neg_idx - dis) % n]
right = balance[(neg_idx + dis) % n]
s = left + right
if s >= need:
ans += need * dis # need 个 1 移动 dis 次
return ans
ans += s * dis # s 个 1 移动 dis 次
need -= s
dis += 1
if __name__ == "__main__":
balance = [1, 2, -5, 2]
result = minMoves(balance)
print(result)
.
#include <iostream>
#include <vector>
#include <cmath>
using namespace std;
long long minMoves(vector<int>& balance) {
int total = 0;
int negIdx = -1;
for (int i = 0; i < balance.size(); i++) {
total += balance[i];
if (balance[i] < 0) {
negIdx = i;
}
}
if (total < 0) { // 总和必须非负
return-1;
}
if (negIdx < 0) { // 没有负数,无需操作
return0;
}
int n = balance.size();
int need = -balance[negIdx];
long long ans = 0;
for (int dis = 1; ; dis++) { // 把与 negIdx 相距 dis 的数移到 negIdx
int left = balance[(negIdx - dis + n) % n];
int right = balance[(negIdx + dis) % n];
int s = left + right;
if (s >= need) {
ans += static_cast<long long>(need) * dis; // need 个 1 移动 dis 次
return ans;
}
ans += static_cast<long long>(s) * dis; // s 个 1 移动 dis 次
need -= s;
}
}
int main() {
vector<int> balance = {1, 2, -5, 2};
long long result = minMoves(balance);
cout << result << endl;
return0;
}
