hdu 2062 Subset sequence

Subset sequence

Time Limit: 1000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 3730    Accepted Submission(s): 1876


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
 

Author
LL
 

Source
校庆杯Warm Up

ANALYSIS

当n=2的时候
子序列从小到大
{1},
{1, 2},
{2},
{2, 1}
当n=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}

不难发现,An可以按首数字分成n组,而每组里除了第一项,剩下的就是An-1的子集合了。
∴f(n) = n[f(n-1) + 1]
 

f(1) = 1

我们拿测试数据3 10来做个示范,解释一下怎么求解。
因为n=3,所以开始数组里1、2、3三个数。
我们知道,n=2时,有4种排列,所以上面n=3可以分成三组,每组5个(加上空集)。 //空集就是说去掉该组第一个数字后为空集
因此第10个在第二组里。所以第一个是2,把2输出。原来的数组里删除2,变成1、3两个数。然后10 - (2 - 1) * 5 =5,即它在第2组的第5个。 //请自行推导所在位置
减去首个空集合,5 - 1 = 4 ≠ 0,表示2后面还有数字。
因为A1 = 1是,所以再第2组里又可以分成两组,每组2个(加上空集)。
所以,4在第2组,剩下的数组中,第二个元素是3,所以输出3。再把数组里的3删除,剩下1一个数。
然后4 - (2 - 1) * 2 = 2,既它是第2组的第2个。
减去首个空集,2 - 1 = 1 ≠ 0,表示2后面还有数字。
按上面的方法继续下去,直到n = 0 或 后面为空集为止。
最后输出数组里的第1个元素,就得到2 3 1,就是解了。从上面的计算可以看出来,本题目的关键是先求的An中每一组的个数g(n)
不难得出:g(n) = f(n) / n
∵f(n) = n[f(n-1) + 1]
∴g(n) = n[f(n-1) + 1] / n = f(n-1) + 1
∵f(n-1) = (n-1) * g(n-1)
∴g(n) = (n-1) * g(n-1) + 1


//分析参照了http://blog.csdn.net/lianqi15571/article/details/8877014博主的分析,写得很清晰啊。

上代码:
(今天练习赛自己撸出来的)

#include <stdio.h>
#include <cstring>
int main()
{
    int a;
    __int64 b;
    __int64 g[30];
    g[1]=1;
    g[2]=2;
    for(int i=3;i<22;i++)
    {
        g[i]=g[i-1]*(i-1)+1;//求出g;
    }
    
    int num[30];
    while(scanf("%d%I64d",&a,&b)!=EOF)  //b的数可能很大,所以用I64d,,用int运行错了
    {
        memset(num,0,sizeof(num));//清零
        for(int i=1;i<=a;i++)
            num[i]=i;        //将要输出的数字保存在数组里
        int am=a;
        while(a--)//总共有a个数(1---a)最多可能输出a个
        {
            int n=b%g[a+1]? b/g[a+1]+1: b/g[a+1];       //我们用b取余对应的g,,得到要输出的数在第几组
            if(num[n]==0)
                break;
            if(am-1==a)
            printf("%d",num[n]);       /  /输出对应的组代表的数字
            else
            printf(" %d",num[n]);
            for(int i=n;i<=a;i++)
                num[i]=num[i+1];            //将刚输出的数字删掉,,留下剩下的数字,,没用的数字都变成0了
            b=(b-g[a+1]*(n-1)-1);   //把问题缩小到下一组,,, 求出在下一组的位置;循环;直到要输出的num为0,说明所有的数都输完了;
        }
        printf("\n");
    }
    return 0;
}


         
之前参照网友代码写的:
             http://blog.sina.com.cn/s/blog_76d8077d0100wyqj.html

#include <stdio.h>
int main()
{
 

   intn,i,j,x[21];
    __int64m,f[30]={0},t;
   for(i=1;i<21;i++)
    {
       f[i]=(i-1)*f[i-1]+1;
    }
   while(scanf("%d%I64d",&n,&m)!=EOF)
    {
       for(i=0;i<=n;i++)
       {
           x[i]=i;
       }
       while(n-- && m)
       {
           t=m/f[n+1]+(m%f[n+1]?1:0);
           printf("%d",x[t]);
           for(i=t;i<=n;x[i]=x[i+1],i++);   
           {
               m-=(t-1)*f[n+1]+1;
           }
           putchar(m?' ':'\n');
       }                                    
    }
    return0;
}


  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值