题解:洛谷 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

【核心思想】

  1. 问题分析:给定 \(n\) 名同学的身高 \(h_i\) 和体重 \(w_i\),需要按身高从高到低、身高相同则体重从重到轻排序。每次只能交换相邻两位同学,求最少交换次数。这是一个逆序对计数问题,核心在于理解相邻交换的最少次数等于序列的逆序对数量。

  2. 算法选择

    • 冒泡排序模拟:通过冒泡排序过程统计交换次数,每次相邻交换消除一个逆序对
    • 自定义比较器:先按身高降序,身高相同按体重降序
  3. 关键步骤

    • 读入数据:读取 \(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\)
  4. 时间/空间复杂度

    • 时间复杂度:\(O(n^2)\),冒泡排序的双重循环
    • 空间复杂度:\(O(n)\),存储结构体数组
  5. 逆序对与相邻交换的核心思想

    • 逆序对的定义:对于排序目标(身高降序、体重降序),若 \(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
posted @ 2026-07-17 14:02  团爸讲算法  阅读(12)  评论(0)    收藏  举报