csp信奥赛C++高频考点专项训练之贪心算法 --【线性扫描贪心】:士兵站队

csp信奥赛C++高频考点专项训练之贪心算法 --【线性扫描贪心】:士兵站队

题目描述

在一个划分成网格的操场上, n n n 个士兵散乱地站在网格点上,由整数坐标 ( x , y ) (x,y) (x,y) 表示。

士兵们可以沿网格边上、下、左、右移动一步,但在同时刻任一网格点上只能有 1 名士兵。

按照军官的命令,他们要整齐地列成一个水平队列,即排成队列,即排成 ( x , y ) , ( x + 1 , y ) , ... , ( x + n − 1 , y ) (x,y),(x+1,y),\ldots,(x+n-1,y) (x,y),(x+1,y),...,(x+n−1,y)。请求出如何选择 x x x 和 y y y 的值才能使士兵们以最少的总移动步数排成一列。

输入格式

输入的第一行是一个整数,代表士兵数 n n n。

第 2 2 2 到 ( n + 1 ) (n + 1) (n+1) 行,每行 2 2 2 个整数,第 ( i + 1 ) (i + 1) (i+1) 行的整数 x i , y i x_i, y_i xi,yi 代表第 i i i 个士兵的坐标。

输出格式

输出一行一个整数,代表答案。

输入输出样例 1
输入 1
复制代码
5
1 2
2 2
1 3
3 -2
3 3
输出 1
复制代码
8
说明/提示

对于 100 % 100\% 100% 的数据,保证 1 ≤ n ≤ 10 4 1 \leq n \leq 10^4 1≤n≤104, − 10 4 ≤ x , y ≤ 10 4 -10^4 \leq x,y \leq 10^4 −104≤x,y≤104。

思路分析

士兵只能沿网格上下左右移动,最终要排成水平连续的一行:(x, y), (x+1, y), ..., (x+n-1, y)

总移动步数 = 所有士兵纵向移动步数之和 + 所有士兵横向移动步数之和。

两个方向可以独立求解。

1. 纵向(y 方向)

所有士兵最终 y 坐标相同,设为 y0

移动步数 = ∑|y_i - y0|

根据绝对值函数性质,y0 取所有 y_i中位数时总和最小。

  • y 数组排序,中位数 = y[n/2](0‑based 下标)。
  • 计算 ∑ ∣ y i − y m e d i a n ∣ ∑|y_i - y_{median}| ∑∣yi−ymedian∣ 得到纵向步数。
2. 横向(x 方向)

最终 x 坐标是连续整数:x0, x0+1, ..., x0+n-1

设士兵按初始 x 坐标从小到大排序为 x1 ≤ x2 ≤ ... ≤ xn

为了总移动步数最小,应让第 i 小的士兵移动到 x0 + i(i 从 0 开始),否则路径交叉会增加步数。

于是横向步数 = ∑ ∣ x i − ( x 0 + i ) ∣ = ∑ ∣ ( x i − i ) − x 0 ∣ ∑|x_i - (x0 + i)| = ∑|(x_i - i) - x0| ∑∣xi−(x0+i)∣=∑∣(xi−i)−x0∣。

令 a i = x i − i a_i = x_i - i ai=xi−i,问题变为求 ∑ ∣ a i − x 0 ∣ ∑|a_i - x0| ∑∣ai−x0∣ 的最小值。

同理,x0a_i 的中位数时总和最小。

  • x 数组排序。
  • 构造 a[i] = x[i] - i(i 从 0 开始)。
  • a 排序,中位数 = a[n/2]
  • 计算 ∑ ∣ a i − a m e d i a n ∣ ∑|a_i - a_{median}| ∑∣ai−amedian∣ 得到横向步数。
3. 总步数

纵向步数 + 横向步数 = 最终答案。

时间复杂度 O(n log n),空间复杂度 O(n),满足 n ≤ 10 4 n ≤ 10^4 n≤104 的要求。


代码实现

cpp 复制代码
#include<bits/stdc++.h> 
using namespace std;

int n;// 士兵数量
int x[10005], y[10005]; // 坐标数组

int main(){
    cin>>n; // 输入士兵数
    for(int i=0;i<n;i++) cin>>x[i]>>y[i]; // 输入每个士兵的坐标

    // ---------- 纵向处理 ----------
    sort(y, y+n); // y坐标升序排序
    int my = y[n/2]; // 取中位数(0‑based下标)
    long long ans = 0; // 总步数,用long long防止溢出
    for(int i=0;i<n;i++) ans += abs(y[i] - my); // 累加纵向步数

    // ---------- 横向处理 ----------
    sort(x, x+n); // x坐标升序排序
    for(int i=0;i<n;i++) x[i] -= i; // 构造 a[i] = x[i] - i
    sort(x, x+n); // 对a数组排序
    int mx = x[n/2]; // 取中位数作为最优x0
    for(int i=0;i<n;i++) ans += abs(x[i] - mx); // 累加横向步数

    cout<<ans<<endl; // 输出最少总步数
    return 0;
}

功能分析

  • 输入处理 :读取士兵数量 n 和每个士兵的坐标 (x, y)
  • 纵向最优化 :通过对 y 坐标排序并取中位数,计算出所有士兵移动到同一行的最少步数。
  • 横向最优化 :先对 x 排序,再变换为 a[i] = x[i] - i,排序后取中位数,得到排成连续水平列的最少横向移动步数。
  • 结果输出:将纵向与横向步数相加,输出总最少步数。
  • 正确性保证:利用了绝对值函数取中位数最优的性质以及排序不等式,确保不出现交叉移动,同时忽略"同一时刻网格点不能有两人"的限制(经典结论:该限制不影响最少步数,可通过调整移动顺序避免冲突)。
  • 性能 :排序时间复杂度 O(n log n),对于 n ≤ 10 4 n ≤ 10^4 n≤104 可在毫秒级完成;使用 long long 避免中间结果溢出。

各种学习资料,助力大家一站式学习和提升!!!

cpp 复制代码
#include<bits/stdc++.h>
using namespace std;
int main(){
	cout<<"##########  一站式掌握信奥赛知识!  ##########";
	cout<<"#############  冲刺信奥赛拿奖!  #############";
	cout<<"######  课程购买后永久学习,不受限制!   ######";
	return 0;
}

【秘籍汇总】(完整csp信奥赛C++学习资料):

1、csp/信奥赛C++,完整信奥赛系列课程(永久学习):

https://edu.csdn.net/lecturer/7901 点击跳转

2、CSP信奥赛C++竞赛拿奖视频课:

https://edu.csdn.net/course/detail/40437 点击跳转

https://edu.csdn.net/course/detail/41081 点击跳转

3、csp信奥赛高频考点知识详解及案例实践:

CSP信奥赛C++动态规划:
https://blog.csdn.net/weixin_66461496/category_13096895.html点击跳转

CSP信奥赛C++标准模板库STL:
https://blog.csdn.net/weixin_66461496/category_13108077.html 点击跳转

信奥赛C++提高组csp-s知识详解及案例实践:
https://blog.csdn.net/weixin_66461496/category_13113932.html 点击跳转

4、csp信奥赛冲刺一等奖有效刷题题解:

CSP信奥赛C++初赛及复赛高频考点真题解析(持续更新): https://blog.csdn.net/weixin_66461496/category_12808781.html 点击跳转

信奥赛C++提高组csp-s初赛&复赛真题题解(持续更新):
https://blog.csdn.net/weixin_66461496/category_13125089.html 点击跳转

5、GESP C++考级真题题解:

GESP(C++ 一级+二级+三级)真题题解(持续更新):https://blog.csdn.net/weixin_66461496/category_12858102.html 点击跳转

GESP(C++ 四级+五级+六级)真题题解(持续更新):https://blog.csdn.net/weixin_66461496/category_12869848.html 点击跳转

GESP(C++ 七级+八级)真题题解(持续更新):
https://blog.csdn.net/weixin_66461496/category_13117178.html 点击跳转

· 文末祝福 ·

cpp 复制代码
#include<bits/stdc++.h>
using namespace std;
int main(){
	cout<<"跟着王老师一起学习信奥赛C++";
	cout<<"    成就更好的自己!       ";
	cout<<"  csp信奥赛一等奖属于你!   ";
	return 0;
}
相关推荐
柠檬07112 小时前
记录bug :C++调用python 路径问题
c++·python·bug
无限进步_2 小时前
二叉树的中序遍历(非递归实现)
开发语言·数据结构·c++·windows·算法·visual studio
王老师青少年编程2 小时前
csp信奥赛C++高频考点专项训练之贪心算法 --【线性扫描贪心】:糖果传递
c++·刷题·贪心·csp·信奥赛·线性扫描贪心·糖果传递
计算机安禾2 小时前
【数据结构与算法】第48篇:算法思想(三):贪心算法
c语言·开发语言·数据结构·算法·贪心算法·代理模式·图论
BestOrNothing_20152 小时前
C++零基础到工程实战(4.3.1):数组与vector初识——连续内存与动态数组的本质解析
c++·vector·初始化·内存分配·栈区数组·堆区数组
_深海凉_2 小时前
LeetCode热题100-爬楼梯
算法·leetcode·职场和发展
j_xxx404_2 小时前
力扣C++算法:哈希表(存在重复元素|存在重复元素II|字母异位词分组)
算法·leetcode·散列表
穿条秋裤到处跑2 小时前
每日一道leetcode(2026.04.17):镜像对之间最小绝对距离
算法·leetcode
codebrick2 小时前
408 数据结构:快排 / 堆排 / 归并 / 希尔 等排序算法对比(复杂度、稳定性、真题考点
数据结构·考研·算法·排序算法·408