1. 问题分析
该问题的本质是一个带有守恒约束的线性变换问题。
- 操作特性:每次操作选择两个不同的索引
,使
减 1,同时
加 1。这一操作在数学上等价于在数组元素之间进行“单位增量的转移”。
- 和的不变性(Invariant):无论执行多少次操作,数组
的元素总和
始终保持不变。
- 目标状态:通过最少次数的单位转移,使得对于所有
,都有
。
2. 算法:不变量与偏差抵消
基于上述分析,我们可以推导出:
-
判定必要条件(可达性分析): 要使
能够转化为
,最基本的物理前提是两者的总和必须相等。若
,则无论执行多少次操作,均无法达成目标,直接返回 -1。
-
贪心与偏差计算: 定义
为位置
处的偏差量:
- 若
,说明该位置存在“盈余”,需要移出
个单位。
- 若
,说明该位置存在“亏空”,需要移入
个单位。
由于每次操作固定将 1 个单位从某处移至另一处,每一次操作最多能同时抵消 1 个单位的盈余和 1 个单位的亏空。
- 若
-
最优性证明: 最小操作次数等于总盈余量(或总亏空量的绝对值)。
设
,设
。
当满足
时,必然有
。
由于一次操作能让
减少 1 且让
减少 1,因此达到目标状态(即
)所需的最少步数即为
。
3. 复杂度分析
- 时间复杂度:
。算法仅需遍历数组两次:一次用于求和校验,一次用于计算偏差。
- 空间复杂度:
。除了存储输入数组外,仅需常数级别的变量用于计数,具有极高的空间利用率。
4. 代码实现
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n;
cin >> n;
vector<ll> a(n), b(n);
ll S1 = 0;
ll S2 = 0;
for (int i = 0; i < n; i++) {
cin >> a[i];
S1 += a[i];
}
for (int i = 0; i < n; i++) {
cin >> b[i];
S2 += b[i];
}
if (S1 != S2) {
cout << -1 << endl;
return 0;
}
ll ops = 0;
for (int i = 0; i < n; i++) {
if (a[i] > b[i]) {
ops += a[i] - b[i];
}
}
cout << ops << endl;
}

京公网安备 11010502036488号