用长度从1至N的N块木板来围成一个围栏。要求是围栏成波浪形,即每块木板要么比它两边的木板都低(低位)要么比它两边的木板都高(高位)。现对所有符合要求的排列方式进行排序。排序规则是从第一块木板开始计算,越短的排名越前,前面的相等,向后依次比较。(即字典序)先给出N和一个指定的数字m,求符合要求的排列中的第m个。
输入:第一行一个正整数表示测试用例数。接下每行为一个测试用例,含两个数字分别表示N和m。
输出:指定的木板排列方案。 如图为n=4的所有情况
Sample Input
2
2 1
3 3
Sample Output
1 2
2 3 1

首先类似于倍增优化dp,我们用试填法确定排名为c的栅栏各木板长度。
我们首先可以枚举第1块木板的长度,设为h,后面n-1块木板构成的总方案数为t,
若t>=c,则说明第1块木板长就为h,继续尝试确定第2块木板长度,否则c-=t,h增加1,重复上述判断。
然则(如此那么)我们可以求出答案,现在我们需要预处理t的值
设f[i,j,k]表示用i块长度不同的木板构建栅栏,最左边的木板从小到大排第j位,其状态为k(k为0表示低位,k为1表示高位)
f[i,j,0]=∑p=j~i-1f[i-1,p,1]
f[i,j,1]=∑p=1~j-1f[i-1,p,0]
long long f[21][21][2],m;
bool used[21];
void prepare()
{
int i,j,k;
f[1][1][0]=f[1][1][1]=1;
for(i=2;i<=20;i++)
for(j=1;j<=i;j++)
{
for(k=j;k<=i-1;k++)f[i][j][0]+=f[i-1][k][1];
for(k=1;k<=j-1;k++)f[i][j][1]+=f[i-1][k][0];
}
}
接下来开始试填,记上一块木板长last,上一块高低位置为k,
1:k^=1;
2:枚举i的长度len,进行判断,找出确定的len
3:i加1,重复步骤
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<queue>
using namespace std;
long long f[21][21][2],m;
bool used[21];
void prepare()
{
int i,j,k;
f[1][1][0]=f[1][1][1]=1;
for(i=2;i<=20;i++)
for(j=1;j<=i;j++)
{
for(k=j;k<=i-1;k++)f[i][j][0]+=f[i-1][k][1];
for(k=1;k<=j-1;k++)f[i][j][1]+=f[i-1][k][0];
}
}
int main()
{
int t,n,i,j,k,last,len;
scanf("%d",&t);
prepare();
while(t--)
{
scanf("%d%lld",&n,&m);
memset(used,0,sizeof(used));
for(j=1;j<=n;j++)//第一块木板单独处理
{
if(f[n][j][1]>=m)
{
last=j;k=1;
break;
}
else m-=f[n][j][1];
if(f[n][j][0]>=m)
{
last=j;k=0;
break;
}
else m-=f[n][j][0];
}
used[last]=1;printf("%d ",last);
for(i=2;i<=n;i++)
{
k^=1;j=0;
for(len=1;len<=n;len++)
{
if(used[len])continue;
j++;
if((k==0&&len<last)||(k==1&&len>last))//合法性
{
if(f[n-i+1][j][k]>=m){last=len;break;}
else m-=f[n-i+1][j][k];
}
}
used[last]=1;
printf("%d ",last);
}
puts("");
}
return 0;
}
来源:https://www.cnblogs.com/dsb-y/p/11200593.html