洛谷1293题解析:加权中位数城市选址问题的C++解法

一、题目解读
洛谷1293题要求解决一个城市选址问题:给定多个城市的学生人数与到莫斯科的距离,需找到一个城市作为中心点,使得所有学生到该中心的加权距离之和最小。题目输入包含城市名称、学生数和到莫斯科的单向距离,莫斯科本身作为终点(距离为0),需处理特殊输入格式(如Windows换行符)。输出为最优城市的名称及最小总距离。
二、解题思路
1. 关键洞察:总距离最小化的位置必定是某个城市,而非中间点,因此可通过枚举所有城市作为中心点计算总花费。
2. 优化方向:利用加权中位数特性,快速定位候选位置。
3. 贪心选择:在多个最小花费位置中,优先选择距离莫斯科最近的城市,确保结果唯一性。
三、解题步骤
1. 数据输入与预处理
定义City结构体存储学生数、距离和名称。
使用getline处理带空格的城市名,并特殊处理Windows换行符(末尾\r字符)。
通过cin.fail()判断输入结束,确保莫斯科为最后一条记录。
2. 排序与总学生数计算
按距离升序排序,确保莫斯科位于数组末尾(距离0)。
遍历累加所有城市的学生数,得到总人数total。
3. 寻找加权中位数位置
二分查找思想:从左到右遍历城市,累计学生数count,当count*2≥total时,当前城市为加权中位数候选位置。
该位置保证两侧学生数权重均衡,总距离最小。
4. 计算最小总花费并筛选候选城市
遍历每个城市i,计算其作为中心点的总花费(Σ|city[i].distance - city[j].distance| * city[j].students)。
维护最小花费min_cost及候选位置列表candidates,若新花费更小则更新,相同则加入列表。
5. 贪心选择最优城市
在候选列表中,选择距离莫斯科最近(即排序后下标最小)的城市作为最终答案。
四、代码与注释
#include <iostream>
#include <vector>
#include <algorithm>
#include <climits>
using namespace std;
struct City {
int students;
int distance;
string name;
// 构造函数处理输入格式
City() {
cin >> students >> distance;
while(cin.peek() == ') cin.get(); // 跳过空格
getline(cin, name); // 读取城市名
// 处理Windows换行符
if(!name.empty() && name.back() == '\r')
name.pop_back();
}
};
int main() {
vector<City> cities;
// 特殊处理莫斯科必为最后一条记录
while(true) {
City city;
if(cin.fail()) break; // 输入结束时退出循环
cities.push_back(city);
if(city.distance == 0) break; // 莫斯科(距离为0)出现时提前终止
}
// 按距离排序确保莫斯科在最后
sort(cities.begin(), cities.end(), [](const City& a, const City& b) {
return a.distance < b.distance;
});
// 计算总学生数
int total = 0;
for(auto& c : cities) total += c.students;
// 寻找加权中位数位置
int median_pos = 0;
int count = 0;
for(; median_pos < cities.size(); ++median_pos) {
count += cities[median_pos].students;
if(count * 2 >= total) break;
}
// 计算最小总花费(允许有多个候选城市)
vector<int> candidates;
int min_cost = INT_MAX;
for(int i = 0; i < cities.size(); ++i) {
int cost = 0;
for(auto& c : cities)
cost += abs(c.distance - cities[i].distance) * c.students;
if(cost < min_cost) {
min_cost = cost;
candidates.clear();
candidates.push_back(i);
} else if(cost == min_cost) {
candidates.push_back(i);
}
}
// 选择距离莫斯科最近的城市
int best = 0;
for(int i = 1; i < candidates.size(); ++i)
if(cities[candidates[i]].distance < cities[candidates[best]].distance)
best = i;
cout << cities[candidates[best]].name << " " << min_cost << endl;
return 0;
}五、总结
本解法通过结合加权中位数的数学性质与贪心策略,将复杂选址问题转化为线性时间复杂度的高效求解。代码中巧妙处理了输入格式兼容性(如Windows换行符)与莫斯科的特殊位置,确保算法的鲁棒性。关键点在于理解“中位数位置即为最优解候选”这一核心逻辑,并通过枚举验证候选点的总花费。该思路对同类加权选址问题具有通用性,适合竞赛选手与编程学习者掌握。
原创内容 转载请注明出处






