乍看该题似乎无法下手,但是我们注意到题目所给的操作次数非常大, 而且看它的两个操作,是不是感觉很熟悉? 没错,冒泡排序的复杂度正是O(n^2) 。
该题正是冒泡排序的改进版 。 但是题目要求我们的是将一个1~n的升序序列变成给定序列,这是不好操作的,我们不妨用逆思维来想,由所给序列变成升序排列应该也是可行的。 但是问题在于操作只有两个,是规定好的,那么我们相应的也要变一下,将操作2变成从队列尾拿一个数放到队首 。 操作一并没有规定顺序,所以不需要改变 ,下面我们要做的就是怎么样可以变成升序的 。
我们受到冒泡排序的思想的启发(仅仅比较相邻两个元素,百度百科上有详细的介绍),我们可以不断比较前两个元素,使之成为升序,然后从队尾添加一个元素到队首,重复以上操作 。 这样,最坏的情况是:每个元素都与其他元素比较了大小 。 所以,显然每个元素的顺序也将正确的升序排列 。
但是值得注意的是:如果按照这个方法,第三组样例会出现错误,陷入了死循环 。。原因很神奇。。我们不妨手动模仿一下,可以发现,在一个循环之中,1和其他三个元素全部比较了一遍,恰好返回原状 !换句话说,1总是在队首,因为它是最小的 。因此,我们将这个特例特判一下,当n在队首时不交换前两个元素 。 至于为什么这样是对的,还是不太明白,明白之后再将详细证明奉上 。
代码如下:
#include<bits/stdc++.h>
using namespace std;
int n,a[305],ans[100000000];
list<int> q;
int main() {
while(~scanf("%d",&n)&&n) {
q.clear();
for(int i=0;i<n;i++) {
scanf("%d",&a[i]);
q.push_back(a[i]);
}
list<int> :: iterator it;
list<int> :: iterator io;
bool ok = true; int cnt = 1,rear = 0;
for(int i=0;i<2*n*n;i++) {
ok = true; cnt = 1;
for(it = q.begin();it!=q.end();++it) {
if((*it)!=cnt) { ok = false; break; }
cnt++;
}
if(ok) break;
it = q.begin();
io = it; ++it;
if(*io!=n && *it < *io) { int c = *io; *io = *it; *it = c; ans[rear++] = 1; }
ok = true; cnt = 1;
for(it = q.begin();it!=q.end();++it) {
if((*it)!=cnt) { ok = false; break; }
cnt++;
}
if(ok) break;
int c = q.back();
q.pop_back(); q.push_front(c); ans[rear++] = 2;
}
for(int i=rear-1;i>=0;i--) printf("%d",ans[i]);
printf("\n");
}
return 0;
}