题解:学而思编程 懒惰的猫咪
【题目来源】
学而思编程:懒惰的猫咪
【题目描述】
小猴有 \(n\) 只猫咪,编号从 \(1\) 到 \(n\)。每只猫咪都有自己的地盘,初始时第 \(i\) 只猫咪在第 \(i\) 个位置上。它们在自己的位置待了很长时间了,已经产生厌倦,所以小猴想重新安排它们的位置,这样就不会有猫咪和之前一样还在同一个位置。但是小猴也很懒,所以他想尽量缩短移动的总距离。
例如,有 \(3\) 只猫咪,初始时它们的位置分别是 \(1,2,3\);\(1\) 号猫咪向右移动两个位置,\(2\) 号猫咪向左移动一个位置,\(3\) 号猫咪向左移动一个位置。经过移动之后猫咪们的位置分别是 \(2,3,1\),并且这个移动的总距离为 \(2 + 1 + 1=4\)。
请你帮助小猴计算一下移动所有猫咪的最短总距离,并按照从左到右的位置顺序输出每一个位置上最后是那一只猫咪,如果有多种移动方式,输出其中字典序最小的一种。
【输入】
第一行包含整数 \(T\),表示共有 \(T\) 组测试数据。
每组测试数据,一行一个整数 \(n\)。
【输出】
每组数据第一行一个整数,表示移动所有猫咪的最短总距离;
第二行 \(n\) 个整数,第 \(i\) 个整数表示移动之后第 \(i\) 个位置上猫咪的编号。
【输入样例】
2
2
3
【输出样例】
2
2 1
4
2 3 1
【核心思想】
-
问题分析:给定 \(n\) 只猫咪,初始位置 \(i\) 上是第 \(i\) 号猫咪。要求重新安排使得没有猫咪留在原位(错位排列),且移动总距离最短。若多种方案,输出字典序最小的排列。这是一个构造错位排列 + 最小距离优化问题,关键在于利用相邻交换实现最小移动距离,并分类处理奇偶性。
-
算法选择:
- 相邻交换策略:相邻两猫交换位置,每只移动距离为 \(1\),总距离最小
- 奇偶分类构造:\(n\) 为偶数时完全相邻交换;\(n\) 为奇数时,前 \(n-3\) 个相邻交换,最后 \(3\) 个特殊循环
-
关键步骤:
- 初始化:读取 \(T\)(测试组数)
- 处理每组数据:
- 读取 \(n\)
- 若 \(n = 1\):
- 输出
1(距离) - 输出
1(唯一排列)
- 输出
- 若 \(n\) 为偶数:
- 输出
n(总距离 = \(n/2 \times 2 = n\),每对交换距离和为 \(2\)) - 遍历 \(i\) 从 \(1\) 到 \(n-1\),步长 \(2\):
- 输出
i+1, i:相邻交换
- 输出
- 输出
- 若 \(n\) 为奇数且 \(n > 1\):
- 输出
n + 1(总距离,最后 \(3\) 个特殊排列距离为 \(4\)) - 遍历 \(i\) 从 \(1\) 到 \(n-3\),步长 \(2\):
- 输出
i+1, i:前 \(n-3\) 个相邻交换
- 输出
- 输出
n-1, n, n-2:最后 \(3\) 个循环移位(\(n-2 \to n-1 \to n \to n-2\))
- 输出
-
时间/空间复杂度:
- 时间复杂度:\(O(T \times n)\),每组数据线性输出
- 空间复杂度:\(O(1)\),仅需常数变量
-
构造策略的核心思想:
- 最小距离原理:相邻交换使每只猫咪移动距离为 \(1\),是单步最小移动,总距离 \(n\)(偶数)为理论最小
- 奇数处理:奇数时无法完全配对,最后 \(3\) 个元素需循环移位(\(a \to b \to c \to a\)),距离为 \(4\)(\(1+1+2\) 或 \(1+2+1\)),总距离 \(n+1\)
- 字典序最小:相邻交换从 \(1\) 开始,保证前面的数尽可能小;最后 \(3\) 个用
n-1, n, n-2而非其他循环排列,确保字典序最小 - 错位排列:所有猫咪都不在原位,满足题意
- 适用于"错位排列 + 距离最小化"类问题,相邻交换是构造最优解的贪心策略
【算法标签】
模拟
【代码详解】
#include <bits/stdc++.h>
using namespace std;
int T; // 测试用例数量
int n; // 数组大小
int main()
{
cin >> T; // 输入测试用例数量
while (T--) // 处理每个测试用例
{
cin >> n; // 输入数组大小n
if (n == 1) // 特殊情况:n=1
{
cout << 1 << endl; // 输出逆序对数量
cout << 1 << endl; // 输出排列
}
else if (n % 2 == 0) // 情况2:n为偶数
{
cout << n << endl; // 输出逆序对数量
// 生成排列:相邻交换
for (int i = 1; i < n; i += 2)
{
cout << i + 1 << " " << i << " "; // 输出交换后的相邻对
}
cout << endl; // 换行
}
else // 情况3:n为奇数且n>1
{
cout << n + 1 << endl; // 输出逆序对数量
// 处理前n-3个数:相邻交换
for (int i = 1; i < n - 2; i += 2)
{
cout << i + 1 << " " << i << " "; // 输出交换后的相邻对
}
// 处理最后三个数:特殊排列
cout << n - 1 << " " << n << " " << n - 2 << endl;
}
}
return 0; // 程序正常结束
}
【运行结果】
2
2
2
2 1
3
4
2 3 1
浙公网安备 33010602011771号