题目
http://codeforces.com/contest/1375/problem/D
思路
由于每次放入的都是MEX,也就是说如果放入了MEX,则这个数不会再成为MEX(除非又被换出来)。
如果我们每次把MEX放到下标为MEX的位置,则我们可以不断把MEX归位。且不会再有同样的值来替换已经归位的数(因为它不再是MEX)。这样我们每次操作可以归位一个数。
但由于一共n+1个数,放入n个位置中,所以最大的那个数n将没有地方放。我们把最大的数放入n-1这个位置中(后来想想其实这步没有必要)。但假如n-1的位置中已经放置了n-1这个数,则我们找到一个没有被归位的位置,把n放进去。下次的MEX一定不是n,于是可以归位一个数。这样我们两次操作可以归位一个数。所以以上解法是可以在2n次操作内解决的。
每次找到MEX同时验证是否是合法解,需要一次遍历。当n无法放入n-1位置时,会再进行一次遍历。2n次操作则时间复杂度为O(n^2)。由于n最大为1000,所以已经可以解决,无需进一步优化。
代码
代码均为提交通过版本。为保持比赛时原样,没有后期优化或者修改。但为方便阅读,可能会增加注释。
#include <iostream>
#include <vector>
using namespace std;
int main() {
int tcase;
std::ios::sync_with_stdio(false);
cin >> tcase;
while(tcase--) {
int n;
cin >> n;
int num[1002];
for(int i = 0; i < n; i++) {
cin >> num[i];
}
vector<int> seq;
while(true) {
vector<int> mask(n + 1);
bool is_valid = true;
// 记录哪些数出现过,并验证是否是非下降数组.
for(int i = 0; i <n; i++) {
mask[num[i]] = 1;
if (i > 0) {
if (num[i] < num[i - 1]) {
is_valid = false;
}
}
}
if (is_valid) {
break;
}
int mex;
for(int i = 0; i <= n; i++) {
if (mask[i] == 0) {
mex = i;
break;
}
}
if (mex != n) {
// 归位
num[mex] = mex;
seq.push_back(mex + 1);
} else {
if (num[n - 1] == n - 1) {
// 找到一个还没有归位的位置放进去。
for(int i = 0; i < n; i++) {
if (num[i] != i) {
num[i] = mex;
seq.push_back(i + 1);
break;
}
}
} else {
// 直接放入n-1的位置。也可以不要这一步,都放入没有归位的位置。
num[n - 1] = mex;
seq.push_back(n);
}
}
}
cout << seq.size() << endl;
for(int i = 0; i < seq.size(); i++) {
cout << seq[i] << " ";
}
cout << endl;
}
return 0;
}