题目大意:
给你一个长度为 \(n\),值域在 \([1,n]\) 的序列 \(a\),每次操做能够将一个位置的数替换为当前序列的 \(MEX\),请你让序列变成一个单调不降的序列,要求操做数在 \(2n\) 之内。
输出操做数和每次操做对应的位置,不要求操做数最少。code
既然不要求操做数最少,那么咱们就能够尝试构造一个特殊的序列,好比 \(0,0,\cdots,0\),\(0,1,\cdots,n-1\) 或 \(1,2,\cdots,n\) 等。
第一种实现太难了些,因此咱们尝试后两种。
以 \(1,2,\cdots,n\) 为例。
首先,当一个数符合要求后,咱们显然不会去改它。就算是出现了其余数都符合要求(指知足咱们想要的结果,下同),剩一个数不一样的状况,咱们只要先让他变成当前的 \(MEX\),若是当前 \(MEX\) 恰好是咱们想要的,就直接赋值完事,不然的话这个 \(MEX\) 必定为 \(0\),那么赋值以后就又变回第一种状况了,由于其余的数都已经把前面的那些数都填掉了。
接下来咱们对 \(MEX\) 分类讨论。get
当 \(MEX \not= 0\) 时,咱们直接让 \(a_{MEX}=MEX\) 便可。
当 \(MEX = 0\) 时,咱们就把他赋到一个不符合要求的数中去。string
这样最后必定能获得咱们想要的序列,接下来来检验他可否在 \(2n\) 次操做内完成。
显然一个数最多只会被操做两次。
由于当把这个数赋值为 \(0\) 后,除非让他符合要求,不然不会改变他,由于这时 \(MEX\) 不可能会等于 \(0\)(只有等于 \(0\) 时才可能对他再作一次操做)。
因而操做次数最多为 \(2n\),符合题意。it
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; int T,n,a[1005]; bool over[1005];//over[i]表示a[i]是否符合要求 int cnt,len,ans[2005];//cnt存有多少个数符合要求,len和ans存答案。 int main() { scanf("%d",&T); while(T--) { scanf("%d",&n); cnt=len=0; memset(over,false,sizeof(over)); for(int i=1;i<=n;i++) { scanf("%d",&a[i]); if(a[i]==i) over[i]=true,cnt++; } while(cnt<n) { int mex; bool m[1005]; memset(m,0,sizeof(m)); for(int i=1;i<=n;i++) m[a[i]]=true; for(int i=0;i<=n;i++) if(!m[i]){mex=i;break;} if(mex==0) { for(int i=1;i<=n;i++) if(!over[i]){a[i]=0;ans[++len]=i;break;} continue; } a[mex]=mex; over[mex]=true; ans[++len]=mex; cnt++; } printf("%d\n",len); for(int i=1;i<=len;i++) printf("%d ",ans[i]); puts(""); } return 0; }