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!
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");
}
}