题意:给一个数列,问通过操作:a[i]=a[i]%x(x自己选择)后,能不能成为1到n的排序,如果能,输出最小操作次数,否则输出-1.
解法:对数组a排序后,先把a中出现的1~n的数删掉最多一个,并标记,因为这个数对可以不经过操作而作为答案的一份子。然后遍历排序后的数组,k为当前的位置应该能操作后得到的数,当a[i]不能变成k,直接输出-1.
对a[i]取模可以得到的数范围0~(a[i]-1)/2
#include<bits/stdc++.h>
#define fo(i,a,b) for(int i=a;i<=b;i++)
using namespace std;
typedef long long ll;
int a[100005],b[100005];
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
int t, n;
cin >> t;
while (t--) {
cin >> n;
int pan=1, ans = 0,k=1;
for (int i = 1; i <= n; i++) { cin >> a[i]; b[i] = 0; }
sort(a + 1, a + 1 + n);
deque<int>q;
for (int i = 1; i <= n; i++) {
if (a[i] <= n && b[a[i]] == 0) {
b[a[i]] = 1;
continue;
}
q.push_back(a[i]);
}
while (k <= n && pan) {
while (1) {
while (b[k])k++;
if (k > n)break;
if (q.size() == 0) { pan = 0; break; }
int res = q.front(); q.pop_front();
if (res < k) { pan = 0; break; }
else if (res == k) { k++; break; }
else {
if (res > k * 2) { ans++; k++; break; }
else {
pan = 0; break;
}
}
}
}
if (pan)cout << ans << endl;
else cout << -1 << endl;
}
return 0;
}