题目大意:题目讲得很复杂,其实数学模型很简单。就是 1~n 这 n 个数,若给出一个排列 a1,a2,a3…an ,对所有 i 满足 (ai-a(i+1))(ai-a(i-1))>0 (其实也就是数按大小的波浪形排列),则该排列符合要求。各个排列之间按字典序排序。(当 n=3 时,满足条件的序列依次为( 1 , 3 , 2 ),( 2 , 1 , 3 ),( 2 , 3 , 1 ),( 3 , 1 , 2 ))
输入 N,C, 要求输出 n=N 时的第 C 个符合要求的序列。
分析:有 100 个测试数据,直接搜索不行。我们通过计算区间,从前往后依次确定各个位置的数字。
先做预处理, T[i][j]表示 当 n=i , a1=j ( 1<=j<=n )时,存在的符合前面要求的序列的个数 。这样就可以根据数组 T[N][] 和 C 的值,得到 a1 的值。当a1的值确定了, C-= ∑ T[n][j](1<=j<=a1-1),n=N-1 , 问题的规模变为了 N-1 , 得到了一个子问题。可是,当 a1!=1&&a1!=n, 后面的数就不连续了,这时候还能把它当成子问题来处理吗?答案是肯定的,我们只要在心里想,把后面 N-1 个数中,所有大于 a1 的数都减 1 。这样,数又会连续了。我们就可以继续根据 T [N-1][]来确定 n=N-1 时的第一个数 的值,也即a2,以此类推,直到确定所有数。需要注意的是,因为数必须成波浪形,在实际的代码中,我们用 M,W 两个数组来取代 T , M[n][k] 表示 a1=k,a2>a1 时的情况, W[n][k] 表示 a1=k,a2<a1 时的情况。因为是按字典序排列,对相同的 k ,我们总是优先考虑 W 。
W , M 的计算可以采用如下递推方法:
M[n] [x] = ∑ W[n-1] [i] (1<=i<=x-1)
W[n] [x] = ∑ M[n-1] [i] (x<=i<=n-1)( 我们只要在心里想 , 把后面 N-1 个数中 , 所有大于等于 x 的数都加 1)
M[n] [x] = W[n] [n-x+1] ( 长度从小到大排列的木条 , 第 x 根与第 n-x+1 根是对应的 )
需要指出的是,在放第一个数的时候, W 和 M 方式都要考虑,而后面的数,则根据前面的情况选择是 W 还是 M 。还有,在最后输出的时候,也会稍微有点纠结。
// pku1037.cpp : Defines the entry point for the console application. // #include "stdafx.h" #include <iostream> using namespace std; long long W[21][21]; long long M[21][21]; int ans[21]; bool used[21]; void Init(); void Cal(int N,long long C); void Print(int N); int main() { Init(); int K,N; long long C; scanf("%d",&K); while(K--) { scanf("%d %I64u",&N,&C); Cal(N,C); Print(N); } return 0; } void Init() { memset(W,0,sizeof(W)); memset(M,0,sizeof(M)); W[1][1]=1; M[1][1]=1; for(int i=2;i<=20;i++) { for(int j=1;j<=i;j++) { W[i][j]=W[i][j-1]+M[i-1][j-1]; M[i][i-j+1]=W[i][j]; } } } void Cal(int N,long long C) { bool flag=true;//flag=ture,下一步为增加;flag=false,下一步为减少。 for(int i=1;i<=20;i++) { if(C<=W[N][i]+M[N][i]) { ans[1]=i; if(C<=W[N][i]) { flag=true; } else { flag=false; C-=W[N][i]; } break; } else { C-=W[N][i]+M[N][i]; } } for(int i=2;i<=N;i++) { if(flag) { for(int j=1;j<ans[i-1];j++) { if(C<=M[N-i+1][j]) { ans[i]=j; flag=false; break; } else { C-=M[N-i+1][j]; } } } else { for(int j=ans[i-1];j<=N-i+1;j++) { if(C<=W[N-i+1][j]) { ans[i]=j; flag=true; break; } else { C-=W[N-i+1][j]; } } } } } void Print(int N) { memset(used,0,sizeof(used)); int k,th; printf("%d",ans[1]); used[ans[1]]=true; for(int i=2;i<=N;i++) { th=0; k=ans[i]; while(k) { if(!used[++th]) { k--; } } used[th]=true; ans[i]=th; printf(" %d",ans[i]); } printf("/n"); }