当前位置: 首页 > news >正文

P1053 [NOIP 2005 提高组] 篝火晚会

题目描述

佳佳刚进高中,在军训的时候,由于佳佳吃苦耐劳,很快得到了教官的赏识,成为了“小教官”。在军训结束的那天晚上,佳佳被命令组织同学们进行篝火晚会。一共有 n 个同学,编号从 1 到 n。一开始,同学们按照 1,2,⋯,n 的顺序坐成一圈,而实际上每个人都有两个最希望相邻的同学。如何下命令调整同学的次序,形成新的一个圈,使之符合同学们的意愿,成为摆在佳佳面前的一大难题。

佳佳可向同学们下达命令,每一个命令的形式如下:

(b1​,b2​,...bm−1​,bm​)

这里 m 的值是由佳佳决定的,每次命令 m 的值都可以不同。这个命令的作用是移动编号是 b1​,b2​,⋯,bm​ 的这 m 个同学的位置。要求 b1​ 换到 b2​ 的位置上,b2​ 换到 b3​ 的位置上,……,要求 bm​ 换到 b1​ 的位置上。执行每个命令都需要一些代价。我们假定如果一个命令要移动 m 个人的位置,那么这个命令的代价就是 m。我们需要佳佳用最少的总代价实现同学们的意愿,你能帮助佳佳吗?

输入格式

第一行是一个整数 n,表示一共有 n 个同学。

其后 n 行每行包括 2 个不同的正整数,以一个空格隔开,分别表示编号是 1 的同学最希望相邻的两个同学的编号,编号是 2 的同学最希望相邻的两个同学的编号,……,编号是n的同学最希望相邻的两个同学的编号。

输出格式

一个整数,为最小的总代价。如果无论怎么调整都不能符合每个同学的愿望,则输出 −1。

输入输出样例

输入 #1复制

4
3 4
4 3
1 2
1 2

输出 #1复制

2

说明/提示

  • 对于 30% 的数据,满足 n≤1000;
  • 对于 100% 的数据,满足 3≤n≤50000。

【题目来源】

NOIP 2005 提高组第三题


题目解析

题目描述了一个班级同学围坐在篝火周围的情景。初始时,同学们按1~n的顺序顺时针围坐。现在给出两个目标排列方式,要求通过最少的交换操作(每次可以交换任意两个人的位置)将初始排列转换为其中一个目标排列。我们需要计算这个最小的交换次数。

算法分析

关键思路

这道题的核心在于理解排列变换的最小交换次数计算。对于排列变换问题,最小交换次数等于排列长度减去排列中循环的个数。这是因为:

  1. 每个长度为k的循环需要k-1次交换来解开

  2. 总交换次数 = Σ(每个循环的长度-1) = n - 循环个数

解题步骤

  1. 构建目标排列:读取并存储两个目标排列

  2. 检查特殊情况:判断两个目标排列是否互为逆序

  3. 计算循环个数:对于每个目标排列,计算初始排列变换到该排列的循环个数

  4. 求最小交换次数:根据循环个数计算最小交换次数,取两个目标排列中的较小值

代码实现

cpp

#include <iostream>
#include <cstring>
using namespace std;const int MAXN = 50005; // 最大人数int n; // 总人数
int a[MAXN], b[MAXN]; // 两个目标排列
int pos[MAXN]; // 位置映射数组
bool vis[MAXN]; // 访问标记数组// 读取并构建目标排列
void read(int arr[]) {for (int i = 1; i <= n; ++i) {cin >> arr[i];}
}// 计算到目标排列的最小交换次数
int calc(int target[]) {memset(vis, false, sizeof(vis)); // 初始化访问标记memset(pos, 0, sizeof(pos)); // 初始化位置映射// 建立目标排列的位置映射for (int i = 1; i <= n; ++i) {pos[target[i]] = i;}int cycle = 0; // 循环计数器for (int i = 1; i <= n; ++i) {if (!vis[i]) {cycle++; // 发现新循环int j = i;// 标记整个循环while (!vis[j]) {vis[j] = true;j = pos[j];}}}return n - cycle; // 最小交换次数
}int main() {cin >> n;read(a); // 读取第一个目标排列read(b); // 读取第二个目标排列// 检查两个排列是否互为逆序bool is_rev = true;for (int i = 1; i <= n; ++i) {if (a[i] != b[n - i + 1]) {is_rev = false;break;}}// 计算两个目标排列的最小交换次数int ans1 = calc(a);int ans2 = calc(b);// 输出较小值cout << min(ans1, ans2) << endl;return 0;
}

代码解析

数据结构

  1. a[MAXN] 和 b[MAXN]:存储两个目标排列

  2. pos[MAXN]:建立从同学编号到其在目标排列中位置的映射

  3. vis[MAXN]:标记同学是否已被访问,用于循环检测

核心函数

  1. read()函数:

    • 功能:读取并存储目标排列

    • 参数:目标排列数组

    • 实现:简单循环读取

  2. calc()函数:

    • 功能:计算到目标排列的最小交换次数

    • 参数:目标排列数组

    • 实现步骤:
      a. 初始化访问标记和位置映射
      b. 建立目标排列的位置映射
      c. 检测循环个数
      d. 返回n-cycle作为最小交换次数

主函数逻辑

  1. 读取输入数据

  2. 检查特殊排列情况(逆序)

  3. 分别计算两个目标排列的最小交换次数

  4. 输出较小值

复杂度分析

  • 时间复杂度:O(n)

    • 读取输入:O(n)

    • 逆序检查:O(n)

    • 循环检测:O(n)

    • 总体线性复杂度

  • 空间复杂度:O(n)

    • 使用了多个大小为n的数组

    • 空间使用与输入规模成正比

优化与注意事项

  1. 逆序检查优化

    • 两个互为逆序的排列具有相同的最小交换次数

    • 检测到这种情况可以节省一次calc计算

  2. 边界条件处理

    • n=1时直接返回0

    • 确保数组访问不越界

  3. 循环检测技巧

    • 使用vis数组避免重复计算

    • 通过位置映射快速找到下一个元素

数学原理深入

排列与循环分解

任何排列都可以分解为若干不相交的循环。例如:
排列(2,4,1,3)可以分解为两个循环:(1→2→4→3→1)和(5→5)

交换次数计算

将一个排列转换为另一个排列的最小交换次数等于n-c,其中:

  • n是排列长度

  • c是循环个数

这是因为:

  • 恒等排列有n个循环(每个元素自成一个循环)

  • 每次交换最多能将循环个数增加1

  • 因此需要至少n-c次交换

实例分析

假设n=4,初始排列为(1,2,3,4)

目标排列1:(2,1,4,3)

  • 循环分解:(1→2→1), (3→4→3)

  • 循环个数:2

  • 最小交换次数:4-2=2

目标排列2:(4,3,2,1)

  • 循环分解:(1→4→1), (2→3→2)

  • 循环个数:2

  • 最小交换次数:4-2=2

总结

本题通过排列的循环分解,巧妙地计算了最小交换次数。关键在于:

  1. 理解排列的循环结构

  2. 掌握位置映射的建立方法

  3. 实现高效的循环检测算法

这种基于循环分解的方法不仅适用于本题,也是解决许多排列相关问题的通用思路。

http://www.lryc.cn/news/615446.html

相关文章:

  • Linux学习--软件编程(shell命令)
  • 多线程(四) --- 线程安全问题
  • 使用 Ansys Discovery 进行动态设计和分析
  • js零基础入门
  • HashTable, HashMap, ConcurrentHashMap
  • Java 8 特性
  • 力扣(删除有序数组中的重复项I/II)
  • 20250808组题总结
  • 力扣 hot100 Day70
  • 力扣-35.搜索插入位置
  • SwiftUI 登录页面键盘约束冲突与卡顿优化全攻略
  • AI推理的“灵魂五问”:直面2025算力鸿沟与中国的破局之路
  • Java基础语法全面解析:从入门到掌握
  • MySQL 复制表详细说明
  • 三极管在电路中的应用
  • SpringSecurity过滤器链全解析
  • 工具箱许愿墙项目发布
  • Redis 事务机制
  • Mysql笔记-系统变量\用户变量管理
  • 机器学习 K-Means聚类 无监督学习
  • 数据结构初阶(7)树 二叉树
  • BGP笔记
  • 机器学习DBSCAN密度聚类
  • 讯飞晓医-讯飞医疗推出的个人AI健康助手
  • 复杂环境下车牌识别准确率↑29%:陌讯动态特征融合算法实战解析
  • 编译技术的两条演化支线:从前端 UI 框架到底层编译器的智能测试
  • Office安装使用?借助Ohook开源工具?【图文详解】微软Office产品
  • 每周算法思考:栈与队列
  • 【数据结构入门】栈和队列
  • 物理AI与人形机器人:从实验室到产业化的关键跨越