题意:有n个1xi的木棒(i从1到n)。把它们排成一排,要求为“波浪形”,即对于第i个木棒要满足a[i-1]<a[i]、a[i]>a[i+1] 或者a[i-1]>a[i]、a[i]<a[i+1]。将这写排列按字典序排序后输出第k个排列。
分析:首先dp出以i(1~n)开头的满足要求的序列有几个,分为2种情况:“121”型和“212”型;然后可以求出k处于以哪 个数字开头的哪种类型中,在从第2位开始以相同的方法计算直到第n位。
dp[i][len][0]表示以i开头长度为len且为“121”型的排列数目,则:dp[i][len][0]=sum(dp[j][len-1][1])(j从i到len-1);
dp[i][len][1]表示以i开头长度为len且为“212”型的排列数目,则:dp[i][len][1]=sum(dp[j][len-1][0])(j从1到i);
由于每次存储i的都是从之前的比如12345种选出3后,剩下的1245对应的1234这种所确定的,所以要输出返回映 射为1~n的数。
#include<cstdio>
#include<cstring>
long long int m,dp[21][21][2];
void get_dp()
{
int i,j,k;
memset(dp,0,sizeof(dp));
dp[1][1][0]=dp[1][1][1]=1;
for(j=2; j<=20; j++)
for(i=1; i<=j; i++)
{
for(k=i; k<j; k++)
dp[i][j][0]+=dp[k][j-1][1];
for(k=1; k<i; k++)
dp[i][j][1]+=dp[k][j-1][0];
}
}
int main()
{
bool flag,used[21];
int T,n,i,j,k,ans[21];
get_dp();
scanf("%d",&T);
while(T--)
{
scanf("%d%lld",&n,&m);
long long sum=0;
int tmp,len=2,tn=n;
for(i=1; i<=tn; i++)
{
if(sum+dp[i][tn][0]+dp[i][tn][1]>=m)
{
ans[1]=i;
tmp=i;
m-=sum;
break;
}
sum+=(dp[i][tn][0]+dp[i][tn][1]);
}
if(m<=dp[tmp][tn][1])flag=true;
else
{
m-=dp[tmp][tn][1];
flag=false;
}
tn--;
while(tn>0)
{
if(flag)
{
for(i=1; i<tmp; i++)
{
if(dp[i][tn][0]>=m)
{
tmp=i;
ans[len++]=i;
break;
}
m-=dp[i][tn][0];
}
}
else
{
for(i=tmp; i<=tn; i++)
{
if(dp[i][tn][1]>=m)
{
tmp=i;
ans[len++]=i;
break;
}
m-=dp[i][tn][1];
}
}
tn--;
flag=!flag;
}
memset(used,0,sizeof(used));
for(i=1; i<=n; i++)
for(j=1; j<=n; j++)
if(!used[j])
{
ans[i]--;
if(ans[i]==0)
{
printf("%d ",j);
used[j]=true;
break;
}
}
printf("\n");
}
return 0;
}