【洛谷 P1116 车厢重组】超详细题解|冒泡排序原理 + 逆序对完整推理

一、题目简介

1. 题目链接

洛谷 P1116 车厢重组(普及 -) https://www.luogu.com.cn/problem/P1116

2. 题意描述

有一列编号混乱的火车车厢,我们只能交换相邻两节车厢。 规定:每一次相邻交换,算作 1 次操作。 求:将整个车厢序列从小到大排序,需要的最少操作次数

3. 输入输出格式

输入:

  • 第一行:整数 n 代表车厢数量
  • 第二行:n 个整数,表示当前车厢的混乱序列

输出:

  • 一个整数:最少相邻交换次数

4. 数据范围

,数据规模较小,完全可以用冒泡排序暴力通过。

二、核心难点推理(全文最重要)

1. 什么是逆序对?

简单直白理解:前面的数比后面的数大,就是逆序对

示例:序列 3 2 1 逆序对包含:(3,2)、(3,1)、(2,1) → 共计 3 组

2. 关键结论(本题核心真理)

只允许相邻交换时,排序的最少交换次数 = 整个序列的逆序对总数

3. 为什么一定相等?(完整逻辑推理)

我们单独分析一次相邻交换的作用: 假设存在相邻逆序组合:5 3 两者交换后变为有序组合:3 5

可以得出核心规律:每一次相邻交换,有且仅有一组逆序对被消除,不会多、不会少

想要让整个无序序列变为完全升序,必须清空序列中所有逆序对。 因此最终推导公式:排序最小相邻交换次数 = 序列逆序对总数量

三、解题算法:冒泡排序

1. 冒泡排序核心原理

冒泡排序是入门级基础排序算法,核心规则:

  1. 反复遍历数组,仅比较相邻两个元素
  2. 若前元素大于后元素,直接交换两者位置
  3. 每一轮遍历,都会将当前未排序区间的最大值冒泡至区间最右侧

本题的限制是只能相邻交换,而冒泡排序的所有操作都是纯相邻交换,完美贴合题目规则。 因此我们只需要统计冒泡排序过程中,所有的交换次数,得到的结果就是本题答案。

2. 双层循环结构详解

// 外层:一共 n-1 轮冒泡
for(int i=1;i<=n-1;i++)
{
    // 内层:只遍历未排序区域
    // 后面 i 个已经排好序,不用再比
    for(int j=1;j<=n-i;j++)
    {
        // 发现逆序,相邻交换
        if(a[j]>a[j+1])
        {
            swap(a[j],a[j+1]);
            cnt++; // 统计交换次数
        }
    }
}
  • 外层 i:控制冒泡总轮次,n 个元素最多执行 n-1 轮即可完全有序
  • 内层 j:每轮只需遍历前 n-i 个元素,右侧 i 个元素已经排序完成,无需重复比较
  • cnt 计数器:累计所有相邻交换次数,等价于统计逆序对总数

四、完整过程模拟(手把手看懂)

以样例:n=3,序列:3 2 1 为例 初始状态:cnt = 0

第一轮冒泡(i=1):末尾 1 个数就位

  • j=1:3>2,交换,序列变为 [2,3,1],cnt=1
  • j=2:3>1,交换,序列变为 [2,1,3],cnt=2 本轮结束,全局最大值 3 归位末尾。

第二轮冒泡(i=2):末尾 2 个数就位

  • j=1:2>1,交换,序列变为 [1,2,3],cnt=3

数组完全有序,循环结束。 最终答案:3,与序列逆序对数量完全一致。

五、可直接 AC 的完整代码(超详细注释)

#include <iostream>
using namespace std;

// n最大1000,开1005防止数组越界
const int N = 1005;
int a[N];

int main()
{
    int n;
    int cnt = 0; // 统计交换次数(逆序对总数)
    cin >> n;

    // 读入车厢序列
    for(int i = 1; i <= n; i++)
    {
        cin >> a[i];
    }

    // 冒泡排序核心逻辑
    for(int i = 1; i <= n - 1; i++)
    {
        // 每轮只遍历未排序区间
        for(int j = 1; j <= n - i; j++)
        {
            // 检测到逆序对,执行相邻交换
            if(a[j] > a[j+1])
            {
                swap(a[j], a[j+1]);
                cnt++;
            }
        }
    }

    // 输出最小操作次数
    cout << cnt << endl;
    return 0;
}

六、算法复杂度分析

  • 时间复杂度
  • 适用范围:本题,运算量极低,洛谷评测数据稳过
  • 空间复杂度,仅需一维数组存储数据,无额外空间消耗

七、知识点总结与拓展

1. 本题核心考点

冒泡排序算法运用 + 逆序对概念理解 + 贪心最小操作逻辑推导,是编程初学者打通排序算法底层逻辑的经典入门题。

2. 必背核心结论

  1. 只能相邻交换时:数组排序最小交换次数 = 逆序对数量
  2. 每一次合法相邻交换,有且仅消除一组逆序对

3. 进阶优化(拔高拓展)

八、写在最后

绝大多数新手做这道题,只会机械默写冒泡代码,却不懂交换次数为什么就是答案

通过本篇题解可以彻底吃透核心逻辑:冒泡排序的每一次相邻交换,本质都是在消除一组逆序对,这也是本题的解题灵魂。

掌握这个结论后,所有「仅相邻交换、求最小排序次数」的题目,都可以直接秒解。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

杨小码不BUG

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值