HDU 1027 Ignatius and the Princess II(康托逆展开) Ignatius and the Princess II

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 4865    Accepted Submission(s): 2929
Problem Description
Now our hero finds the door to the BEelzebub feng5166. He opens the door and finds feng5166 is about to kill our pretty Princess. But now the BEelzebub has to beat our hero first. feng5166 says, "I have three question for you, if you can work them out, I will release the Princess, or you will be my dinner, too." Ignatius says confidently, "OK, at last, I will save the Princess."
"Now I will show you the first problem." feng5166 says, "Given a sequence of number 1 to N, we define that 1,2,3...N-1,N is the smallest sequence among all the sequence which can be composed with number 1 to N(each number can be and should be use only once in this problem). So it's easy to see the second smallest sequence is 1,2,3...N,N-1. Now I will give you two numbers, N and M. You should tell me the Mth smallest sequence which is composed with number 1 to N. It's easy, isn't is? Hahahahaha......" Can you help Ignatius to solve this problem?
 
Input
The input contains several test cases. Each test case consists of two numbers, N and M(1<=N<=1000, 1<=M<=10000). You may assume that there is always a sequence satisfied the BEelzebub's demand. The input is terminated by the end of file.
 
Output
For each test case, you only have to output the sequence satisfied the BEelzebub's demand. When output a sequence, you should print a space between two numbers, but do not output any spaces after the last number.
 
Sample Input
6 4 11 8
 
Sample Output
1 2 3 5 6 4
1 2 3 4 5 6 7 9 8 11 10
 题意: 给一个整数 N, 然后让求N的全排列中(按字母序排序), 第M小的序列。
分析: N最大可以到1000, 卧槽, 难道要求N! 吗?, 当然不是。 (除非脑子被ACMer碾压成了粉末!), 观察到M最大为 10000, 即:  M< 8!。 那么8的阶乘足矣。
然后就是裸裸的康托展开啦~~~~。(康托展开与康托逆展开
 
康托逆展开
例1 {1,2,3,4,5}的全排列,并且已经从小到大排序完毕
(1)找出第96个数
首先用96-1得到95
用95去除4! 得到3余23
有3个数比它小的数是4
所以第一位是4
用23去除3! 得到3余5
有3个数比它小的数是4但4已经在之前出现过了所以第二位是5(4在之前出现过,所以实际比5小的数是3个)
用5去除2!得到2余1
有2个数比它小的数是3,第三位是3
用1去除1!得到1余0
有1个数比它小的数是2,第二位是2
最后一个数只能是1
所以这个数是45321
(2)找出第16个数
首先用16-1得到15
用15去除4!得到0余15
用15去除3!得到2余3
用3去除2!得到1余1
用1去除1!得到1余0
有0个数比它小的数是1
有2个数比它小的数是3 但由于1已经在之前出现过了所以是4(因为1在之前出现过了所以实际比4小的数是2)
有1个数比它小的数是2 但由于1已经在之前出现过了所以是3(因为1在之前出现过了所以实际比3小的数是1)
有1个数比它小得数是2 但由于1,3,4已经在之前出现过了所以是5(因为1,3,4在之前出现过了所以实际比5小的数是1)
最后一个数只能是2
所以这个数是14352
 
上面就是康托逆展开的例子。 这道题也是这个例子的裸裸的模板! (注意第M大, 和第M小的区别!)
 
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;

int a[9] = {1, 1, 2, 6, 24, 120, 720, 5040, 40320};
int vis[1005];

int main()
{
    int n, m;
    while(scanf("%d%d", &n, &m)!=EOF)
    {
        memset(vis, 0, sizeof(vis));
        m--;
        int temp = 1;
        while(temp<n)
        {
            if((n-temp)<=8)
            {
                int k = m/a[n-temp];
                int mod = m%a[n-temp];
                int count = 0;
                for(int i=1; i<=n; i++)
                {
                    if(!vis[i]) ++count;
                    if((count-1)==k)
                    {
                        printf("%d ", i);
                        vis[i] = 1; break;
                    }
                }
                m = mod;
            }
            else 
            {
                for(int i=1; i<=n; i++)
                {
                    if(!vis[i] == 1)
                    {
                        vis[i] = 1;
                        printf("%d ", i); break;
                    }
                }
            }
            ++temp;
        }
        for(int i=1; i<=n; i++)
            if(!vis[i]) printf("%d
", i);
    }
    return 0;
}
 生成全排列,暴力求解!
#include <stdio.h>
#include <map>
#include <algorithm>
#include <iostream>
using namespace std;

const int maxn = 1010;
int a[maxn];
int main()
{
    int n, m;
    while(scanf("%d%d", &n, &m)!=EOF)
    {
        for(int i=1; i<=n; i++) a[i] = i;
            int num = 1;
        while(num<m)
        {
            next_permutation(a+1, a+1+n);
            num++;
        }
        for(int i=1; i<n; i++)
            printf("%d ", a[i]);
        printf("%d
", a[n]);
    }
    return 0;
}