蓝桥杯3829 大石头的搬运工
问题描述
在一款名为”大石头的搬运工“的游戏中,玩家需要操作一排 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≤103
对于 100% 的测试样例,1≤n≤105,1≤wi ,pi ≤106。
运行限制
语言:C++
最大运行时间:1s
最大运行内存:512M
题目信息
- 难度: 中等
- 标签: 前缀和, 枚举
- 传送门
参考题解
c++版:
//核心在于一堆石头的消耗只需要考虑从起始位置挪动到终点位置
//出于对数据量的考虑,防止超时
//我们可以考虑先对石头的位置进行排序
//通过维护前缀和数组pre[i]用来记录从起始位置到第i个石头全部挪到第i个石头的位置的消耗
//维护后缀和数组nex[i]记录从末尾位置到第i个石头全部挪到第i个石头的位置的消耗
#include <bits/stdc++.h>
#define p first
#define w second
using ll=long long;//注意开ll,防止数据溢出
using namespace std;
const int N=1e5+10;//参考n的范围去定义
int n;
pair<int,int>pr[N];//利用键值对存储,注意pair的默认排序是通过pair.first
ll pre[N],nex[N];
ll sum_w;
ll res=LONG_LONG_MAX;//注意初始化成最大值,以此通过min函数求小值
int main(){
cin>>n;
for(int i=1;i<=n;i++){
cin>>pr[i].w>>pr[i].p;
}
sort(pr+1,pr+n+1);
for(int i=1;i<=n;i++){
pre[i]+=pre[i-1]+sum_w*(pr[i].p-pr[i-1].p);
sum_w+=pr[i].w;
}
sum_w=0;
for(int i=n;i>=1;i--){
nex[i]+=nex[i+1]+sum_w*(pr[i+1].p-pr[i].p);
sum_w+=pr[i].w;
}
for(int i=1;i<=n;i++)
res=min(res,pre[i]+nex[i]);
cout<<res;
return 0;
}
时间复杂度分析
排序的时间复杂度为O(nlogn),计算pre,nex,pre+nex的时间复杂度为O(n)
所以这个方法的时间复杂度为O(nlogn)