版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
题目链接:点击查看
题目大意:给出两列数字,问第二列数是由第一列数怎样排序得到的,题目保证答案唯一,并且只有归并排序或插入排序两种选择
题目分析:因为一开始不太了解归并排序和插入排序的特点,所以有点无从下手,去网上看了一下各自的特点后,就有了模拟的大致方向了,先在这里挂一下两种排序中间过程的特点:(针对这个题目而言)
- 插入排序: 前面的一段连续序列有序,后面剩下的序列与原序列一一对应
- 归并排序:设当前归并长度为len(len为2的幂次,最小为1),则每长度为len的区间内都保证有序
有了这个特点,再加上题目保证了除了归并排序就是插入排序,因为插入排序的特点看起来比较好判断,那么我们可以先判断一下该序列是否是插入排序的中间过程,若是的话,记录一下断点,从头到断点后的一个位置sort一下答案就出来了;若不是插入排序,那么就只能是归并排序了,我们可以先找出当前归并排序的归并长度len,将len乘二后模拟一遍归并排序的规则即可(即每个长度为len的区间都单独排序)
这里有几个细节需要注意一下,我也不知道算不算坑点:
- 题目只是说了要保证升序排序,但没保证是严格升序,虽然两个样例都给出的是严格升序,但是我们在判断的时候都要以非严格升序来判断,不然会有一组测试点过不去
- 在处理归并排序时,若数组长度n无法整除归并长度len,也就是说明最后肯定会剩下一段区间比len要小,这个需要单独排序
- 在获得归并长度len后,我们需要将其乘2后再进行新一轮的排序,此时需要注意一下,若len*2>n的情况下,我们只需要处理到n即可
代码:(自认为还是写的比较清晰易看的)
#include<iostream>
#include<cstdlib>
#include<string>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<climits>
#include<cmath>
#include<cctype>
#include<stack>
#include<queue>
#include<list>
#include<vector>
#include<set>
#include<map>
#include<sstream>
#include<unordered_map>
using namespace std;
typedef long long LL;
const int inf=0x3f3f3f3f;
const int N=110;
int n;
int a[N],b[N];
int check()//判断是否为插入排序
{
b[0]=INT_MIN;
int i;
for(i=1;i<=n;i++)
{
if(b[i-1]>b[i])
break;
}
int mark=i;
for(;i<=n;i++)
if(a[i]!=b[i])
return 0;
return mark;
}
int getlen()//获得归并排序的长度
{
for(int len=1;len<=n;len<<=1)
{
for(int i=1;i+len-1<=n;i+=len)
{
int j=i+len-1;
for(int k=i+1;k<=j;k++)
if(b[k]<=b[k-1])
return len;
}
}
return n;//如果前面的最大长度len都通过了,那么下一次归并排序的长度最大到n结束
}
void print()
{
printf("%d",b[1]);
for(int i=2;i<=n;i++)
printf(" %d",b[i]);
printf("\n");
}
int main()
{
// freopen("input.txt","r",stdin);
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",a+i);
for(int i=1;i<=n;i++)
scanf("%d",b+i);
int mark=check();
if(mark)//插入排序
{
puts("Insertion Sort");
sort(b+1,b+1+mark);
}
else//归并排序
{
puts("Merge Sort");
int len=getlen();
for(int i=1;i+len-1<=n;i+=len)//每长度为len的区间都单独排序
sort(b+i,b+i+len);
sort(b+n/len*len+1,b+1+n);//最后剩余的那段也记得排序
}
print();
return 0;
}