题解:洛谷 B4361 排序
【题目来源】
洛谷:B4361 [GESP202506 四级] 排序 - 洛谷
【题目描述】
体育课上有 \(n\) 名同学排成一队,从前往后数第 \(i\) 位同学的身高为 \(h_i\),体重为 \(w_i\)。目前排成的队伍看起来参差不齐,老师希望同学们能按照身高从高到低的顺序排队,如果身高相同则按照体重从重到轻排序。在调整队伍时,每次只能交换相邻两位同学的位置。老师想知道,最少需要多少次交换操作,才能将队伍调整成目标顺序。
【输入】
第一行,一个正整数 \(n\),表示队伍人数。
接下来 \(n\) 行,每行两个正整数 \(h_i\) 和 \(w_i\),分别表示第 \(i\) 位同学的身高和体重。
【输出】
输出一行,一个整数,表示最少需要的交换次数。
【输入样例】
5
1 60
3 70
2 80
4 55
4 50
【输出样例】
8
【核心思想】
-
问题分析:给定 \(n\) 名同学的身高 \(h_i\) 和体重 \(w_i\),需要按身高从高到低、身高相同则体重从重到轻排序。每次只能交换相邻两位同学,求最少交换次数。这是一个逆序对计数问题,核心在于理解相邻交换的最少次数等于序列的逆序对数量。
-
算法选择:
- 冒泡排序模拟:通过冒泡排序过程统计交换次数,每次相邻交换消除一个逆序对
- 自定义比较器:先按身高降序,身高相同按体重降序
-
关键步骤:
- 读入数据:读取 \(n\) 和数组 \(a[1..n]\)(每个元素包含 \(h\) 和 \(w\))
- 冒泡排序统计交换次数:
- 外层循环 \(i\) 从 \(1\) 到 \(n\)
- 内层循环 \(j\) 从 \(1\) 到 \(n-i\):
- 若 \(a[j].h < a[j+1].h\):交换并 \(cnt \leftarrow cnt + 1\)
- 否则若 \(a[j].h = a[j+1].h\) 且 \(a[j].w < a[j+1].w\):交换并 \(cnt \leftarrow cnt + 1\)
- 输出结果:\(cnt\)
-
时间/空间复杂度:
- 时间复杂度:\(O(n^2)\),冒泡排序的双重循环
- 空间复杂度:\(O(n)\),存储结构体数组
-
逆序对与相邻交换的核心思想:
- 逆序对的定义:对于排序目标(身高降序、体重降序),若 \(i < j\) 但 \(a_i\) 应在 \(a_j\) 之后,则 \((i,j)\) 构成一个逆序对。每个逆序对至少需要一次相邻交换才能消除
- 冒泡排序的最优性:冒泡排序每次相邻交换恰好消除一个逆序对,因此交换次数等于逆序对总数,这是最少交换次数
- 双重关键字排序:先比较身高(主关键字),身高相同再比较体重(次关键字),确保排序结果符合题目要求
- 算法优化空间:\(n\) 较大时,冒泡排序的 \(O(n^2)\) 效率较低,可使用归并排序或树状数组在 \(O(n \log n)\) 时间内统计逆序对
- 适用于相邻交换排序、逆序对统计类基础问题
【算法标签】
普及- #其他排序
【代码详解】
#include <bits/stdc++.h>
using namespace std;
const int N = 3005; // 定义最大数据量
// 定义结构体存储高度和宽度
struct Node
{
int h; // 高度
int w; // 宽度
} a[N]; // 结构体数组
int n; // 物品数量
int cnt = 0; // 交换次数计数器
int main()
{
// 输入物品数量
cin >> n;
// 输入每个物品的高度和宽度
for (int i = 1; i <= n; i++)
cin >> a[i].h >> a[i].w;
// 冒泡排序过程
for (int i = 1; i <= n; i++)
{
// 内层循环,每次减少比较范围
for (int j = 1; j <= n - i; j++)
{
// 如果前一个物品高度小于后一个,交换并计数
if (a[j].h < a[j + 1].h)
{
swap(a[j], a[j + 1]);
cnt++;
}
// 高度相等时,比较宽度
else if (a[j].h == a[j + 1].h && a[j].w < a[j + 1].w)
{
swap(a[j], a[j + 1]);
cnt++;
}
}
}
// 输出总交换次数
cout << cnt << endl;
return 0;
}
【运行结果】
5
1 60
3 70
2 80
4 55
4 50
8c
浙公网安备 33010602011771号