- 将原序列变为
0
0
0到
n
−
1
n-1
n−1的全排列
例子:
2 2 3
MEX(2,2,3)=0
0 2 3
MEX(0,2,3)=1
0 2 1
MEX(0,2,1)=3
- 从后往前,按照如下方法交换(
ps:绝不是因为语言表达能力不好)
example 1:
0 2 1
i == 3, a[i] != i
i == 3, loc == 1
0 2 3
i == 3, loc == 0
1 2 3
loc == 0 结束当前交换
i = 2, a[i] == i
i = 1, a[i] == i
example 2:
2 1 0
i == 3, a[i] != i
i == 3, loc == 0
2 1 3
loc == 0结束当前交换
i == 2, a[i] != i
i == 2, loc == 1
2 0 3
i == 2, loc == 2
1 0 3
i == 2, loc == 0
1 2 3
loc == 0结束当前交换
...
ps:特判i==n的情况
if (i == n) a[i] = i;
else a[i] = 0;
AC代码:
// Test1.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <set>
using namespace std;
const int maxn = 1010;
int n, cnt, T;
int a[maxn], ans[maxn << 1], num[maxn], vis[maxn];
int main() {
cin >> T;
while (T--) {
cnt = 0;
cin >> n;
for (int i = 1; i <= n; i++) {
cin >> a[i];
num[a[i]]++;//记录出现次数
vis[a[i]] = 1;//记录是否出现
}
for (int i = 1, j; i <= n; i++) {
if (num[a[i]] == 1 && a[i] < n) continue;//只有一个且小于n就不动
num[a[i]]--;//当前个数减一
if (num[a[i]] == 0) vis[a[i]] = 0;//如果为0,则标记为没出现过
for (j = 0; j < n; j++)
if (!vis[j])
break;//找到MEX就跳出
a[i] = j, vis[j] = 1, num[j]++;
ans[++cnt] = i;//统计答案
}//第一步
for (int i = n; i >= 1; i--) {
if (a[i] == i) continue;
int loc = a[i], tmp;
if (i == n) a[i] = i;//特判
else a[i] = 0;
ans[++cnt] = i;
while (loc) {//交换
tmp = loc;
loc = a[tmp], a[tmp] = tmp, ans[++cnt] = tmp;//交换
}
}//第二步
cout << cnt << endl;
for (int i = 1; i <= cnt; i++)
cout << ans[i] << " ";
cout << endl;
for (int i = 0; i <= n; i++)//替下一次初始化
num[i] = vis[i] = 0;
}
return 0;
}