hdu 2062 Subset sequence
Problem Analyse
考虑一个集合 An = { 1, 2, ..., n}。比如,A1={1},A3={1,2,3}。我们称一个非空子集元素的排列为一个子集序列。对所有的子序列按字典顺序排序。你的任务就是给出第m个子序列。 |
首先我们来看看An一共有多少个子集。n=1时,只有{1}一个子集合也许你发现规律了。An子集合的个数为: C1n·A11 + C2n·A22 + ... + Cnn·Ann 这个公式是对的。但我们换个角度看。 n=3时,有不难发现,An可以按首数字分成n组,而每组里除了第一项,剩下的就是An-1的子集合了。 ∴f(n) = n[f(n-1) + 1] f(1) = 1 我们拿测试数据3 10来做个示范,解释一下怎么求解。 从上面的计算可以看出来,本题目的关键是先求的An中每一组的个数g(n) |
#include <stdio.h> int main() { int i,n,t;//n:一共多少元素<=20。t:所求子集位于分组后的第几组 __int64 m;//位于第几个子集 __int64 c[21]={0};//后面将子集分组后平均每组个数,如:c[2]表示n=2时的分组每组中子集数 int s[21];//后面将子集按字典序分组后每组的初始首元素,组数<=20 for (i=1;i<21;i++) c[i]=c[i-1]*(i-1)+1;//推导出来的c[n]=(n-1)*c[n-1]+1 while (scanf("%d%I64d",&n,&m)!=EOF) { for(i=0;i<21;i++) s[i]=i;//每循环一次就重新归位每组首元素 while (n>0&&m>0) { t=m/c[n]+(m%c[n]>0?1:0); if(t>0)//得到第m个子集在分组后的第t组,若t>0 { printf("%d",s[t]); for(i=t;i<=n;i++) s[i]=s[i+1];//或s[i]+=1,我们发现:第n组中,第2个元素在第n个时变为它的下一个数 m-=((t-1)*c[n]+1);//减去(t-1组总子集数+1),m变为表示在剩余子集中位于第几个 putchar(m==0?'\n':' '); } n--;//依次递减,直到执行上面的if代码或退出 } } return 0; }
- //我的
/*本题关键就在于判断进行到n的子集的第几环了*/ #include<stdio.h> int main() { int i,n,t,s[21]; __int64 m; __int64 c[21]={0}; for(i=1;i<21;i++) c[i]=c[i-1]*(i-1)+1;//由f(n)=(f(n-1)+1)*n推导而来,用来计算f(n)不同首元素下有几组的 while(EOF!=scanf("%d%I64d",&n,&m)) { for(i=0;i<21;i++) s[i]=i;//每输出一次之前来重新归位一次 while(n>0&&m>0) { t=m/c[n]+(m%c[n]>0?1:0);//第m个子集在分组后第t组 if(t>0) { printf("%d",s[t]); for(i=t;i<=n;i++) s[i]=s[i+1];//原来在t位置的被输出过一次,向后顺移一位 m-=((t-1)*c[n]+1);//减去(t-1组总子集数+1(空集)),m变为表示在剩余子集中位于第几个 putchar(m==0?'\n':' '); } n--; } } return 0; }
#include<stdio.h>//自己重新打的版本 int main() { int c[21],i,n,t; __int64 m,s[21]={0}; for(i=1;i<21;i++) s[i]=(i-1)*s[i-1]+1; while(EOF!=scanf("%d%I64d",&n,&m)) { for(i=0;i<21;i++) c[i]=i;//每次读入一组数据都重置数组 while(m&&n) { t=m/s[n]+(m%s[n]>0?1:0); if(t>0)//这里的t判断是否大于零不可省,否则会输出0 { printf("%d",c[t]); for(i=t;i<=n;i++) c[i]=c[i+1]; m-=((t-1)*s[n]+1); putchar(m==0?'\n':' '); } n--; } } return 0; }
#include<stdio.h>//<span style="font-size:14px;color:#FF0000;"><strong>acm第一次网络赛的逆推版本</strong></span> <pre name="code" class="objc">int main() { int c[21],i,j,n,x; __int64 m,num,s[21]={0}; for(i=1;i<21;i++) s[i]=(i-1)*s[i-1]+1; while(EOF!=scanf("%d%I64d",&n,&m)) { num=0; for(i=0;i<=n;i++) c[i]=i;//每次读入一组数据都重置数组 for(i=0;i<m;i++) { scanf("%d",&x); for(j=1;j<=n;j++) { if(c[j]==x) break;} num+=s[n]*(j-1)+1; n--; for(;j<=n;j++)//i不等于x c[j]=c[j+1]; } printf("%I64d\n",num); } return 0; }