#前缀和 #STL #pair
问题描述
在一款名为”大石头的搬运工“的游戏中,玩家需要操作一排 n 堆石头,进行 n−1 轮游戏。。
每一轮,玩家可以选择一堆石头,并将其移动到任意位置。
在n−1 轮移动结束时,要求将所有的石头移动到一起(即所有石头的位置相同)即为成功。
移动的费用为石头的重量乘以移动的距离。例如,如果一堆重量为 2 的石头从位置 3 移动到位置 5 ,那么费用为 2 ×(5 - 3)= 4 .
请计算出所有合法方案中,将所有石头移动到一起的最小费用。
可能有多堆石头在同一个位置上,但是一轮只能选择移动其中一堆。
输入格式
第一行一个整数 n,表示石头的数量。
接下来 n 行,每行两个整数wi和pi,分别表示第 i 个石头的重量和初始位置。
输出格式
输出一个整数,表示最小的总移动费用。
样例输入
3
2 3
3 1
1 5
样例输出
8
说明:
一种最优的移动方式是:
首先,将第一个石头移动到位置 1,费用为 2 × ( 3 − 1) = 4 ;
然后,将第三个石头移动到位置 1,费用为 1 × ( 5 − 1 ) = 4 。
所以最小的总移动费用为 4 + 4 = 8 。
数据范围
对于 20% 的测试样例,1 ≤ n ≤ 10^3;
对于 100% 的测试样例,1 ≤ n ≤ 10^5 , 1 ≤ wi,pi ≤ 10^6.
题解:
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const ll N = 1e5 + 10;
pair<int, int >p[N];
ll pre[N], nex[N];
int main() {
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int n;
cin >> n;
for (int i = 1; i <= n; i++) {
cin >> p[i].second >> p[i].first;
}
sort(p + 1, p + n + 1);
ll s = 0;
for (int i = 1; i <= n; i++) {
pre[i] = pre[i - 1] + s * (p[i].first - p[i - 1].first);
s += p[i].second;
}
s = 0;
for (int i = n; i >= 1; i--) {
nex[i] = nex[i + 1] + s * (p[i + 1].first - p[i].first);
s += p[i].second;
}
ll ans = 1e18;
for (int i = 1; i <= n; i++) {
ans = min(ans, pre[i] + nex[i]);
}
cout << ans << endl;
return 0;
}
总结:
本题分别计算堆中任意一块石头的前后两部分各自的前缀和 pre[i] 和 nex[i] ,通过min找到最小的。
定义了一个pair,用于存储石头的重量和位置两个信息,通过pair的 sort 排序功能 对石头的位置进行排序,便于后面计算前缀和时对距离的计算。
然后分别对pair中的每一个的石头计算 pre[i] ,和nex[i] 。
最后通过min找到最小的费用即可。