poj 1037 经典计数dp+字典序

来源:互联网 发布:linux 别名 编辑:程序博客网 时间:2024/06/11 08:32
<pre name="code" class="html">
点击打开链接

#include <cstdio>#include <iostream>#include <cstring>#define up 0#define down 1using namespace std;typedef long long ll;const int Mod=1e9+7;const int M =35;ll dp[M][M][2];//dp[i][j][up] i根中的第j根开头 前两个为升序的合法排列数 void Init(){memset(dp,0,sizeof(dp));dp[1][1][down]=dp[1][1][up]=1;for(int i=2;i<=20;i++){for(int j=1;j<=i;j++)//枚举开头 {for(int k=1;k<j;k++)//downdp[i][j][down]+=dp[i-1][k][up];for(int k=j;k<=i;k++)//updp[i][j][up]+=dp[i-1][k][down];//(ai ? ai?1)*(ai ? ai+1) > 0}}}void print(int n,ll C){ll skipped=0,oldval;//跳过的方案数int seq[M];int used[M];memset(used,0,sizeof(used));for(int i=1;i<=n;i++){int No=0;int k;for(k=1;k<=n;k++)//枚举位置i的编号 {oldval=skipped;//if(!used[k]){++No;//剩下stick中第几短if(i==1)skipped+=dp[n][No][up]+dp[n][No][down];//跳过字典序比它小的个数 (类似康托的逆运算)else if(k>seq[i-1]&&(i==2||seq[i-1]<seq[i-2])){skipped+=dp[n-i+1][No][down];  }else if(k<seq[i-1]&&(i==2||seq[i-1]>seq[i-2])){skipped+=dp[n-i+1][No][up];}if(skipped>=C) // 小于的话 则表示n位数中第i位以k开头的字典序都小于C 跳过 break;//大于则说明排列为 ..k... }}used[k]=1;seq[i]=k;skipped=oldval;}for(int i=1;i<=n;i++){cout<<seq[i];if(i<n)cout<<" ";elsecout<<endl;} }int main(){Init();//dp对合法序列计数  int t;cin>>t;while(t--){ll n,c;cin>>n>>c;print(n,c);}return 0;}

0 0