一條街上的居民買賣酒,相鄰運輸成本為 1。求最小總運輸成本。這是經典的貪心 + 前綴和問題。
a[i] > 0:第 i 人需買入 a[i] 單位酒。a[i] < 0:第 i 人需賣出 -a[i] 單位酒。記錄所有賣家位置,買家優先從最左邊賣家買入。維護 left 指向當前可用的最左賣家,賣完則右移。
// 核心邏輯
for (i = 0; i < n; i++) {
if (a[i] > 0) { // 買家
for (j = left; j < k; j++) {
if (a[b[j]] + a[i] <= 0) { // 賣家酒足夠
sum += a[i] * abs(b[j] - i);
a[b[j]] += a[i];
a[i] = 0;
if (a[b[j]] == 0) left++;
break;
} else { // 賣家酒不夠
sum += -a[b[j]] * abs(b[j] - i);
a[i] += a[b[j]];
a[b[j]] = 0;
left++;
}
}
}
}
核心洞察:第 i 人將手中酒(正為買入,負為賣出)傳遞給 i+1,成本即為 |prefix[i]|。總成本 = Σ |prefix[i]|。
for (i = 0; i < n - 1; i++) {
sum += abs(a[i]);
a[i + 1] += a[i];
}
考慮酒在街道上流動:每個位置 i 的淨流量為 a[i],流經邊 (i, i+1) 的酒量絕對值即為 |Σ_{k=0}^i a[k]|。無論具體怎麼匹配買賣雙方,酒必須流經這些邊,總成本固定。
#include <iostream>
#include <cstdio>
#include <cmath>
using namespace std;
#define LL long long
int main() {
int n;
LL a[110000];
while (scanf("%d", &n) && n) {
LL sum = 0;
for (int i = 0; i < n; i++) scanf("%lld", &a[i]);
for (int i = 0; i < n - 1; i++) {
sum += abs(a[i]);
a[i + 1] += a[i];
}
printf("%lld\n", sum);
}
return 0;
}
| 方法 | 時間 | 空間 | 備註 |
|---|---|---|---|
| 雙指針模擬 | O(N) | O(N) | 直觀易懂,適合理解過程 |
| 前綴和 | O(N) | O(1) | 極簡代碼,數學本質 |
此題深刻體現了「貪心即最優」與「全局最優等於局部流量和」的等價性,是貪心算法與前綴和技巧的經典結合。