题目链接:http://poj.org/problem?id=1037
题意:给你N个板子,每个板子长度都不一样。长度为1~n,使板子排列成波浪形,即对于对于1< i < n的每个板子,满足第i个板子左右两边的木板都比它高或者低。输出第C个波浪形的序列是多少。
假如不考虑波浪形,那么我们可以知道第C个全排列是多少。
比如:
N=4,C=10;
如果第一个数为1,后面三个数就有3!种排法,C-3!=4>0,说明第一个数不是1,此时C=4.如果第一个数为2,C-3!=-2<=0,那么说明第一个数应该是2。C+3!=4,然后按照前面的方法推算出最后3个数的值。最后可以得到N=4时,第10种全排列的值是多少。
然而题目要求是波浪形的全排列。
我们假定dp[i][len][0]是长度为len的,以第i短的木棒开始,且前两个数是升序的全排列的个数。同理dp[i][len][1]是降序的全排列的个数。i不是长度为i
dp转移见下图
需要确定到底是升序还是降序开头。因此对于第一个数特殊处理一下。
之后出现的数需要判断是剩余的数中第几小。然后按照之前介绍的方法对全排列的每一位进行判断是多少。
#include <iostream>
#include<algorithm>
#include<cstring>
using namespace std;
#define LL long long
LL dp[25][25][2];//0->升序,1->降序;
int mark[25];//检测是否用过
int main()
{
memset(dp,0,sizeof(dp));
dp[1][1][0]=dp[1][1][1]=1;
for(int len=2; len<=20; len++)
{
for(int i=1; i<=len; i++)//枚举第一根木棒长度
{
//枚举第二根木棒长度
for(int j=1; j<i; j++)
dp[i][len][0]+=dp[j][len-1][1];
//枚举第二根木棒长度
for(int j=i; j<len; j++)
dp[i][len][1]+=dp[j][len-1][0];
}
}
int T;
cin>>T;
while(T--)
{
int N;
LL C;
cin>>N>>C;
memset(mark,0,sizeof(mark));
int flag=0;
int l=1,r=N;
int j;
for(int i=1; i<=N; i++)
{
if(flag)
break;
for(j=0; j<2; j++) //确定升序还是降序
{
C-=dp[i][N][j];
if(C<=0)
{
C+=dp[i][N][j];
cout<<i;
mark[i]=1;
if(j==0)
l=1,r=i-1;
else
l=i+1,r=N;
flag=1;
break;
}
}
}
j^=1;
for(int len=N-1; len>=1; len--)
{
int num=0;
for(int i=1;i<l;i++)
{
if(!mark[i])
num++;
}
for(int i=l; i<=r; i++)//i是剩余木棒中第num短的
{
if(!mark[i])
{
num++;
C-=dp[num][len][j];
if(C<=0)
{
C+=dp[num][len][j];
mark[i]=1;
cout<<" "<<i;
if(j==0)
l=1,r=i-1;
else
l=i+1,r=N;
j^=1;
break;
}
}
}
}
cout<<endl;
}
return 0;
}