zju 1089

原题挺长的,不想翻译,就是要用一种比较快的方法从给定的k个有序的数产生6个数的排列,并且这6个数要求也从小到大排列.
前几天刚看了清华大学出版的组合数学一书,正好有讲这种方法的,不过还是做了一段时间.
输入:多行,每行一串数字,第一个是k,后面为k个数字
输出:对应输入的每一行,输出这k个数字中6个数字的排列.

例如:输入 8 1 2 3 5 8 13 21 34
   输出 1 2 3 5 8 13
        1 2 3 5 8 21
        1 2 3 5 8 34
        1 2 3 5 13 21
        1 2 3 5 13 34
        1 2 3 5 21 34
        1 2 3 8 13 21
        1 2 3 8 13 34
        1 2 3 8 21 34
        1 2 3 13 21 34
        1 2 5 8 13 21
        1 2 5 8 13 34
        1 2 5 8 21 34
        1 2 5 13 21 34
        1 2 8 13 21 34
        1 3 5 8 13 21
        1 3 5 8 13 34
        1 3 5 8 21 34
        1 3 5 13 21 34
        1 3 8 13 21 34
        1 5 8 13 21 34
        2 3 5 8 13 21
        2 3 5 8 13 34
        2 3 5 8 21 34
        2 3 5 13 21 34
        2 3 8 13 21 34
        2 5 8 13 21 34
        3 5 8 13 21 34
用数组b来存放产生的排列,a放输入的k个数,注意到b[i]的最大值为a[i+k-6]
仔细观察上面产生排列的规律,不难看出可以用这样的方法产生所需的排列:
   1.令i=6;while(b[i]==a[i+k-6) i--;  即如果b[i]达到了最大值就是i--;
   2.令b[i]等于b[i]后面的一个数,然后调整i后面的数,使其比前面一个数大.
        for(m=i+1;m<=6;m++)  b[m]=a[++j];
        这里j是b[i]在数组a中的下标,定义一个函数findid来求得
   3.重复上面的过程,一直到产生的排列数达到k!/(k-6)!/6!

 

/*zju 1089  快速产生排列
by woodfish
*/

#include 
< stdio.h >
int  a[ 50 ],b[ 50 ],k;
int  findid( int  n) {     //查找n在数组a中的下标
 int i;
 
for(i=1;i<=k;i++
  
if(a[i]==n) return i;
}

void  main() {
 
int i,j,m;
 
long s;
 
 scanf(
"%d",&k);
 
while(k!=0){
 
for(i=1;i<=k;i++{
  scanf(
"%d",&a[i]);
  b[i]
=a[i];
 }


 s
=(k-5)*(k-4)*(k-3)*(k-2)*(k-1)*k/720-1;  //计算总共的排列数
 for(i=1;i<=5;i++) printf("%d ",b[i]);
 
//输入的前6个数就是最开始的排列
 printf("%d\n",b[6]);     
 
for(;s>=1;s--{
  i
=6;
  
while(b[i]==a[i+k-6]) i--;
  j
=findid(b[i]);
  b[i]
=a[++j]; 
  
for(m=i+1;m<=6;m++)
   b[m]
=a[++j];
  
for(m=1;m<=5;m++) printf("%d ",b[m]);
  printf(
"%d\n",b[6]);
 }

 scanf(
"%d",&k);
 
if(k!=0) printf("\n");
 }

}


你可能感兴趣的:(zju 1089)