题目大意:
给你一个n(<=100000),让你找出1~n个数的一个排列,使得该排列的h(n)最小,定义h(n)=max(该排列的最长上升序列的长度,该排列的最长下降序列的长度)。
题解:
这个题首先是打表找规律,直接想的话,对于我来说还是相当困难的。这个题是考察构造法的一个题,且有很多细节需要注意。
官方题解:
应该比较容易能够想到这个答案是 sqrt(N) 级别的,这个结论的证明可以参考吴文虎的那本组合数学 p21 。
当然这还没有结束,怎么找字典序最小的那个解呢?先看看完全平方数吧,对于 4 ,我们有 2 1 4 3 ,对于 9 ,我们有 3 2 1 6 5 4 9 8 7 。嗯,完全平方数的规律还是好找的,那么
5 呢?不就是加一个数么,1 3 2 5 4 ,如果你觉得这是答案,那你就大错特错了,答案是 1 2 5 4 3 。同样,对于 10 ,答案是 1 2 6 5 4 3 10 9 8 7 。
于是我们就可以构造程序,每次以 ceil(sqrt(N)) 为一组,尽量把大的数安排到后面的组中,同时要注意,一定要分出 ceil(sqrt(N)) 组,能让较小的 1 2 这样的数能够排在前面。
最后,这题并不难,但是做到 1Y 也不容易,这就要克服各种逻辑上的问题,形成良好的思维习惯,对于解题来说也是十分重要的。
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
using namespace std;
int n,sec,part,c,x;
int s[111111];
int main()
{
scanf("%d",&sec);
for(int z=1;z<=sec;z++)
{
scanf("%d",&n);
x=n;c=ceil(sqrt(n));
for(part=c;part>=1;part--)
if(part==2 && x<c*2)//细节部分
{
int lim=x;
for(int i=max(lim-c+1,2);i<=lim;i++)
{
s[i]=x;
x--;
}
for(int i=1;i<=max(lim-c,1);i++)
{
s[i]=x;
x--;
}
break;
}
else
{
int lim=x;
for(int i=lim-c+1;i<=lim;i++)
{
s[i]=x;
x--;
}
}
for(int i=1;i<=n-1;i++)
printf("%d ",s[i]);
printf("%d\n",s[n]);
}
return 0;
}