小红的有序数组
[题目链接](https://www.nowcoder.com/practice/d4ee2b59f3564cfcac9a66161bca5a5c)
思路
给定一个长度为 的排列,每次操作可以选择位置奇偶性相同的两个位置并交换它们的值,求将数组排成升序所需的最少操作次数,若无法完成输出
。
判断可行性
位置 (1-indexed)上的元素值
,排好序后应该在位置
。由于只能在同奇偶位置之间交换,元素只能在奇数位置之间移动、或在偶数位置之间移动。因此,
和
的奇偶性必须相同,否则无解,输出
。
计算最少交换次数
判断可行后,问题转化为:给定一个排列,求将其排成有序所需的最小交换次数。
这是一个经典结论:对排列做循环分解,长度为 的环需要
次交换。总交换次数 =
=
。
注意,虽然奇数位置和偶数位置各自独立,但由于整体仍然构成一个排列(可行性已保证每个元素都能到达目标位置),直接对整个排列做循环分解即可——奇数位置上的环和偶数位置上的环自然不会交叉。
样例演示
数组 :
- 检查奇偶性:位置 1 值 1(同奇),位置 2 值 4(同偶),位置 3 值 5(同奇),位置 4 值 2(同偶),位置 5 值 3(同奇)。全部合法。
- 循环分解:
(自环),
(长度 2),
(长度 2)。
- 交换次数:
。
代码
#include <bits/stdc++.h>
using namespace std;
int main(){
int n;
scanf("%d",&n);
vector<int> a(n+1);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
// 检查奇偶性
for(int i=1;i<=n;i++){
if((i%2)!=(a[i]%2)){
printf("-1\n");
return 0;
}
}
// 循环分解求最少交换次数
vector<bool> vis(n+1,false);
int ans=0;
for(int i=1;i<=n;i++){
if(vis[i]||a[i]==i) continue;
int cnt=0, j=i;
while(!vis[j]){
vis[j]=true;
j=a[j];
cnt++;
}
ans+=cnt-1;
}
printf("%d\n",ans);
return 0;
}
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
int[] a = new int[n + 1];
for (int i = 1; i <= n; i++) a[i] = sc.nextInt();
for (int i = 1; i <= n; i++) {
if ((i % 2) != (a[i] % 2)) {
System.out.println(-1);
return;
}
}
boolean[] vis = new boolean[n + 1];
int ans = 0;
for (int i = 1; i <= n; i++) {
if (vis[i] || a[i] == i) continue;
int cnt = 0, j = i;
while (!vis[j]) {
vis[j] = true;
j = a[j];
cnt++;
}
ans += cnt - 1;
}
System.out.println(ans);
}
}
import sys
input = sys.stdin.readline
n = int(input())
a = [0] + list(map(int, input().split()))
for i in range(1, n + 1):
if i % 2 != a[i] % 2:
print(-1)
exit()
vis = [False] * (n + 1)
ans = 0
for i in range(1, n + 1):
if vis[i] or a[i] == i:
continue
cnt = 0
j = i
while not vis[j]:
vis[j] = True
j = a[j]
cnt += 1
ans += cnt - 1
print(ans)
const readline = require('readline');
const rl = readline.createInterface({ input: process.stdin });
const lines = [];
rl.on('line', l => lines.push(l.trim()));
rl.on('close', () => {
const n = parseInt(lines[0]);
const a = [0, ...lines[1].split(' ').map(Number)];
for (let i = 1; i <= n; i++) {
if (i % 2 !== a[i] % 2) {
console.log(-1);
return;
}
}
const vis = new Array(n + 1).fill(false);
let ans = 0;
for (let i = 1; i <= n; i++) {
if (vis[i] || a[i] === i) continue;
let cnt = 0, j = i;
while (!vis[j]) {
vis[j] = true;
j = a[j];
cnt++;
}
ans += cnt - 1;
}
console.log(ans);
});
复杂度分析
- 时间复杂度:
。奇偶性检查和循环分解各遍历一次数组。
- 空间复杂度:
。存储数组和访问标记。

京公网安备 11010502036488号