hdu1027(逆康托展开)

src:http://acm.hdu.edu.cn/showproblem.php?pid=1027

一开始已经提过了,康托展开是一个全排列到一个自然数的双射,因此是可逆的。即对于上述例子,在(1,2,3,4,5)给出61可以算出起排列组合为 34152。由上述的计算过程可以容易的逆推回来,具体过程如下:

  • 用 61 / 4! = 2余13,说明a[5]=2,说明比首位小的数有2个,所以首位为3。
  • 用 13 / 3! = 2余1,说明a[4]=2,说明在第二位之后小于第二位的数有2个,所以第二位为4。
  • 用 1 / 2! = 0余1,说明a[3]=0,说明在第三位之后没有小于第三位的数,所以第三位为1。
  • 用 1 / 1! = 1余0,说明a[2]=1,说明在第二位之后小于第四位的数有1个,所以第四位为5。
  • 最后一位自然就是剩下的数2啦。
  • 通过以上分析,所求排列组合为 34152。

!!!用61/4!不用担心受到3!这一项的影响,因为这一样的值不可能>=4!,因为3!这一项的系数表示第四位前面小于第四位的,易知这个值<=3 !!!

注意:cantors算法给出的序列全排列顺序的下标是从0开始的,所以从题目输入序号后要减一!!!

ac代码:

#include<bits/stdc++.h>
using namespace std;
#define per(i,a,b) for(int i=a;i <= b;i++)
#define max(a,b) a=max(a,b)
#define min(a,b) a=min(a,b)
#define sz(x) (int)x.size()
typedef long long ll;
ll gcd(ll a,ll b){while(b){ll t=b;b=a%b;a=t;}return a;}
const int inf=0x3f3f3f3f;
const int mod=1000000007;
#define siz 40005
static const int FAC[] = {1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880};   // 阶乘
int n,m;
vector<int>v;
void decantor()
{
    vector<int>chs;//存放可选数
    for(int i=1;i<=n;i++)chs.push_back(i);
    int cnt=n;
    while(cnt>0){
        if(cnt<=8){
            int r=m/FAC[cnt-1];
            m%=FAC[cnt-1];
            v.push_back(chs[r]);
            chs.erase(chs.begin()+r);
        }
        else {
            v.push_back(chs.front());
            chs.erase(chs.begin());
        }
        cnt--;
    }
}

int main()
{
#ifndef ONLINE_JUDGE
    freopen("b\\Data_In.txt","r",stdin);
#endif
    while(scanf("%d%d",&n,&m)!=EOF){
        m--;
        v.clear();
        decantor();
        for(int i=0;i<v.size();i++)printf("%d%c",v[i]," \n"[i!=(v.size()-1)?0:1]);
    }
    return 0;
}

 

posted @ 2018-08-16 11:52  WindFreedom  阅读(133)  评论(0编辑  收藏  举报