Subset sequence

Problem Description
Consider the aggregate An= { 1, 2, …, n }. For example, A1={1}, A3={1,2,3}. A subset sequence is defined as a array of a non-empty subset. Sort all the subset sequece of An in lexicography order. Your task is to find the m-th one.
 

Input
The input contains several test cases. Each test case consists of two numbers n and m ( 0< n<= 20, 0< m<= the total number of the subset sequence of An ).
 

Output
For each test case, you should output the m-th subset sequence of An in one line.
 

Sample Input
   
   
   
   
1 1 2 1 2 2 2 3 2 4 3 10
 

Sample Output
   
   
   
   
1 1 1 2 2 2 1 2 3 1

【解析】

     首先,此题是求一个序列的第m个子序列,这个序列的所有子序列是按字典序排序的,举两个个简单的列子:

    序列: An = {1,  2}

    按字典序排列的子序列:{1}、{1,2}、

                                                {2}、{2,1}。

    序列: An = {1,  2, 3}

    按字典序排列的子序列:{1}、{1,2}、{1,2,3}、{1,3}、{1,3,2}、

                                                {2}、{2,1}、{2,1,3}、{2,3}、{2,3,1}、

                                                {3}、{3,1}、{3,1,2}、{3,2},{3,2,1}。

通过上面的列子可以发现,每个子序列是有规律可行的,只要有规律,我们就可以找出这一题的解题方法。我们可以将子序列分成n(n为输入值)组,每组中的每个子序列的首个元素是每组的第一个子序列的元素,然后就是每组的子序列个数相同,所以,接下来我们求第n个子序列就可以先找出这个子序列在第几组,然后又可以通过每组的个数相同,在进一步的将这个子序列的位置精确到这一组的第几个子序列,这是找所求子序列的位置,接下来,我们求目标子序列的元素,我们可以发现求目标子序列的第一个元素很简单,只要求出是第几组,第一个元素就是几,比如输入n = 3  m=10(下面任然用此列),可以求出第m=10个子序列在第二组中,所以这个序列的首个元素就是2,接下里就是求这个子序列是否还存在其他元素,因为我们求出了目标子序列是在第二组的最后一个,然后,子序列集又是按字典序排列的,所以出去2必然存在其他元素; 然后因为我们求出了第一个元素为2,所以在原来的序列中要删除2,也就是在第二组中去掉2,得到序列为:{}、{1}、{1,3}、{3}、{3,1},空集没意义,所以可以去掉,即变为{1}、{1,3}、{3}、{3,1},此时,我们又会发现这个序列集中,又有可以分为两组,每组个数为2,因为我们要求的子序列在变形前第二组的最后一个,即位置为5,所以,此时接下来的目标元素任然在变形后的第二组最后一个序列中,可以通过(5 - 1)/ 2得到,可以看出为3,这是目标子序列的第二个元素,然后继续判断是否还会存在元素,同上一个道理,因为所求目标是第二组最后一个,而且此组有2个序列集,所以必然还有一个元素,我们先将3从第二步得到的序列集中去掉,得到:{1}、{1}、{}、{1},去掉空集,得到{1},所以最后一个为一组,同时最后一个元素也为1,可以用(2  - 1)/1得到其位置,以上是具体思路,下面是具体求解步骤:

求出An有多少个子序列,公式为:f ( n ) = n * [ f ( n - 1 ) + 1 ];

第一步:求出每组有多少个子序列,g ( n ) = f ( n ) / n   =>    g ( n ) = n * [ f ( n - 1 ) + 1] / n = f ( n - 1 ) + 1

                                                                g ( n ) = f ( n ) / n   =>     f ( n ) = g ( n ) * n         

                                                                                                =>     f ( n - 1 ) = g ( n - 1 ) * ( n - 1 )

                                                    =>       g ( n ) = g ( n - 1 ) * ( n - 1 ) + 1(每组子序列集个数);

第二步:将1 ~ 20存入数组a,然后找出所求子序列位置,mid = m / s[n] + (m % s[n] > 0 ? 1 : 0);

第三步:输出所在组的第一个元素 a [ mid ],然后将数组a后面的 元素依次加1,即删除 a [ mid ];

第四步:判断是否还存在元素,这里我们用目标子序列所在位置判断,若不为0,说明还有元素,反之则无。

具体情况请移步代码。

【代码】

#include <iostream>
#include <cstring>
using namespace std;

int main()
{
    int n, mid, a[21];
    __int64  m, s[21];
    memset(a, 0, sizeof(a));
    memset(s, 0, sizeof(s));
    for(int i = 1; i < 21; i++)
            s[i] = (i - 1) * s[i - 1] + 1;//打表求出20种不同长度的序列的子序列的按分组规则后得到的每组子序列的个数
    while(cin >> n >> m)
    {
        for(int j = 1; j <= n; j++)
            a[j] = j;
        while(m > 0 && n > 0)
        {
            mid = m / s[n] + (m % s[n] > 0 ? 1 : 0);//求出所求子序列所在组
            if(mid > 0)
            {
                cout << a[mid];//输出mid组的首个元素
                for(int k = mid; k <= n; k++)
                    a[k] = a[k + 1];//在数组a中删除a[mid]
                m -= ((mid - 1) * s[n] + 1);//找出在剩余子集中所在位置
                if(m == 0)
                    cout << endl;
                else
                    cout << ' ';
            }//当m为0时,结束循环
            n--;
        }
    }
    return 0;
}



你可能感兴趣的:(Subset sequence)