1.练习项目:
问题描述
在一款名为"大石头的搬运工"的游戏中,玩家需要操作一排 n 堆石头,进行 n−1 轮游戏。
每一轮,玩家可以选择一堆石头,并将其移动到任意位置。
在 n−1 轮移动结束时,要求将所有的石头移动到一起(即所有石头的位置相同)即为成功。
移动的费用为石头的重量乘以移动的距离。例如,如果一堆重量为 2 的石头从位置 3 移动到位置 5,那么费用为 2×(5−3)=4。
请计算出所有合法方案中,将所有石头移动到一起的最小费用。
可能有多堆石头在同一个位置上,但是一轮只能选择移动其中一堆。
输入格式
第一行一个整数 n,表示石头的数量。
接下来 n 行,每行两个整数 wi 和 pi,分别表示第 i 堆石头的重量和初始位置。
输出格式
输出一个整数,表示最小的总移动费用。
说明
一种最优的移动方式是:
首先,将第一个石头移动到位置 1,费用为 2×(3−1)=4;
然后,将第三个石头移动到位置 1,费用为 1×(5−1)=4。
所以最小的总移动费用为 4+4=8。
数据范围
对于 20% 的测试样例,1≤n≤1e3。
对于 100% 的测试样例,1≤n≤1e5,1≤wi,pi≤1e6。
2.选择课程
在蓝桥云课中选择课程《16届蓝桥杯省赛无忧班(C&C++ 组)4期》,选择第二章"基础算法"编程20并开始练习。
3.开始练习
(1)前缀和思想:
#include<bits/stdc++.h>
using namespace std;
using ll=long long;
const int N=1e5+10;
pair<ll,ll>a[N];
int main()
{
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
int n;cin>>n;
ll sum1[N],sum2[N];
for(int i=1;i<=n;i++){
cin>>a[i].second>>a[i].first;
}
sort(a+1,a+n+1);
ll s=0;
for(int i=1;i<=n;i++){
sum1[i]=sum1[i-1];
sum1[i]+=s*(a[i].first-a[i-1].first);
s+=a[i].second;
}
s=0;
for(int i=n;i>=1;i--){
sum2[i]=sum2[i+1];
sum2[i]+=s*(a[i+1].first-a[i].first);
s+=a[i].second;
}
ll res=1e18;
for(int i=1;i<=n;i++){
res=min(res,sum1[i]+sum2[i]);
}
cout<<res<<'\n';
return 0;
}
(2)加权平均数思想:
#include<bits/stdc++.h>
using namespace std;
using ll = long long;
int main() {
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int n; cin >> n;
vector<pair<ll, ll>> stones(n); // (位置, 重量)
ll total_weight = 0;
for(int i = 0; i < n; i++) {
ll w, p; cin >> w >> p;
stones[i] = {p, w};
total_weight += w;
}
// 按位置排序
sort(stones.begin(), stones.end());
// 找加权中位数
ll half = (total_weight + 1) / 2; // 向上取整
ll cur_weight = 0;
ll target = 0;
for(auto& [pos, w] : stones) {
cur_weight += w;
if(cur_weight >= half) {
target = pos;
break;
}
}
// 计算总成本
ll cost = 0;
for(auto& [pos, w] : stones) {
cost += w * abs(pos - target);
}
cout << cost << '\n';
return 0;
}
(3)检验结果
对此代码进行检验,检验后无报错,提交此代码,判题结果为正确100分。
(4)练习心得:注意每段代码末尾的分号是否存在,如不存在则需即使补充;输入法是否切换为英语模式;语法是否错误。