找回密码
 立即注册
查看: 36|回复: 0

算法竞赛实战:洛谷P1293城市选址问题的加权中位数解法

[复制链接]
  • 打卡等级:常驻代表
  • 打卡总天数:33
  • 打卡月天数:2
  • 打卡总奖励:223
  • 最近打卡:2025-08-14 15:12:36

39

主题

1

回帖

4597

积分

VIP会员

积分
4597
发表于 2025-7-15 14:42:31 | 显示全部楼层 |阅读模式
一、问题描述与解题思路
洛谷P1293题目要求我们为多个城市选择一个**的**地点,使得所有学生前往该地点的总交通成本**。每个城市有三个属性:学生人数、距离莫斯科的距离(题目规定莫斯科距离为0)以及城市名称。这是一个典型的设施选址问题,可以通过加权中位数算法高效解决。
二、完整代码实现(带详细注释)
  1. #include <iostream>
  2. #include <vector>
  3. #include <algorithm>
  4. #include <climits>
  5. using namespACe std;

  6. // 城市结构体,包含学生人数、距离和名称
  7. struct City {
  8.     int students;
  9.     int distance;
  10.     string name;
  11.    
  12.     // 构造函数处理输入格式
  13.     City() {
  14.         cin >> students >> distance;
  15.         while(cin.peek() == ' ') cin.get();  // 跳过多余空格
  16.         getline(cin, name);
  17.         // 处理Windows换行符
  18.         if(!name.empty() && name.back() == '\r')
  19.             name.pop_back();
  20.     }
  21. };

  22. int main() {
  23.     vector<City> cities;
  24.     // 特殊处理莫斯科必为**一条记录
  25.     while(true) {
  26.         City city;
  27.         if(cin.fail()) break;
  28.         cities.push_back(city);
  29.         if(city.distance == 0) break;  // 莫斯科距离为0,作为结束标志
  30.     }
  31.    
  32.     // 按距离排序确保莫斯科在**
  33.     sort(cities.begin(), cities.end(), [](const City& a, const City& b) {
  34.         return a.distance < b.distance;
  35.     });
  36.    
  37.     // 计算总学生数
  38.     int total = 0;
  39.     for(auto& c : cities) total += c.students;
  40.    
  41.     // 寻找加权中位数位置
  42.     int median_pos = 0;
  43.     int count = 0;
  44.     for(; median_pos < cities.size(); ++median_pos) {
  45.         count += cities[median_pos].students;
  46.         if(count * 2 >= total) break;  // 找到满足条件的**个位置
  47.     }
  48.    
  49.     // 计算最小总花费(允许有多个候选城市)
  50.     vector<int> candidates;
  51.     int min_cost = INT_MAX;
  52.     for(int i = 0; i < cities.size(); ++i) {
  53.         int cost = 0;
  54.         for(auto& c : cities)
  55.             cost += abs(c.distance - cities[i].distance) * c.students;
  56.         
  57.         if(cost < min_cost) {
  58.             min_cost = cost;
  59.             candidates.clear();
  60.             candidates.push_back(i);
  61.         } else if(cost == min_cost) {
  62.             candidates.push_back(i);
  63.         }
  64.     }
  65.    
  66.     // 选择距离莫斯科最近的城市(当有多个候选时)
  67.     int best = 0;
  68.     for(int i = 1; i < candidates.size(); ++i)
  69.         if(cities[candidates[i]].distance < cities[candidates[best]].distance)
  70.             best = i;
  71.    
  72.     cout << cities[candidates[best]].name << " " << min_cost << endl;
  73.     return 0;
  74. }
复制代码

三、算法核心解析
  • 输入处理

    • 使用City结构体处理输入数据,特别处理了Windows换行符问题
    • 莫斯科作为特殊城市(距离为0)作为输入结束标志

  • 数据预处理

    • 按距离排序城市,确保莫斯科在**
    • 计算总学生数,为加权中位数计算做准备

  • 加权中位数计算

    • 遍历排序后的城市列表,累加学生数直到达到总学生数的一半
    • 这个位置就是**候选位置之一

  • 精确计算**解

    • 计算每个候选城市的总交通成本
    • 找出最小成本的所有候选城市
    • 当有多个候选时,选择距离莫斯科最近的城市

四、复杂度分析与优化建议
  • 时间复杂度

    • 排序操作:O(n log n)
    • 加权中位数查找:O(n)
    • 精确计算:O(n²)
    • 总体复杂度:O(n²)

  • 优化建议

    • 可以利用加权中位数性质,只计算中位数附近的几个候选点
    • 对于大规模数据,可以考虑分治算法
    • 可以预处理距离差值,减少重复计算


    • 处理空输入情况
    • 处理所有城市学生数为0的特殊情况
    • 处理多个城市距离相同的情况

来源:大矩学习资料

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

快速回复 返回顶部 返回列表