时间限制: 1 Sec 内存限制: 128 MB
提交: 581 解决: 336
[提交][状态][讨论版][命题人:外部导入]
题目描述
排列与组合是常用的数学方法,其中组合就是从n个元素中抽出r个元素(不分顺序且r < = n),我们可以简单地将n个元素理解为自然数1,2,…,n,从中任取r个数。
现要求你不用递归的方法输出所有组合。
例如n = 5 ,r = 3 ,所有组合为:
1 2 3
1 2 4
1 2 5
1 3 4
1 3 5
1 4 5
2 3 4
2 3 5
2 4 5
3 4 5
输入
一行两个自然数n、r ( 1 < n < 21,1 < = r < = n )。
输出
所有的组合,每一个组合占一行且其中的元素按由小到大的顺序排列,所有的组合也按字典顺序。
解题思路
注意题意,采用非递归的方法来求解该问题。递归方法的话,很简单的回溯法就可以。
最核心问题 是 求解当前组合的下一组合。组合间的顺序由字典序定义。类似n进制数的加法,如何进位 是关键。
AC代码
#include<cstdio>
#include<cstring>
int a[30];
void print(int a[], int length){
//输出
for(int i=1; i<=length; i++){
printf("%d ", a[i]);
}
printf("\n");
}
bool next_combination(int a[], int n, int r){
//按照字典序递增
//按照字典序递增 组合
//判断是否需要进位
if(a[r]<n) {
a[r]++;
}
else { //需要进位时
int i=1;
while(a[r-i]==n-i && r-i>0){ //确定在哪一位进位
i++;
}
if(r-i==0) return false; //溢出,当前为最大组合,返回
a[r-i]++;
while(i){ //进位位后的位依次递增
a[r-i+1]=a[r-i]+1;
i--;
}
}
return true; //无溢出,返回
}
int main(){
int n, r;
while(~scanf("%d%d", &n, &r)){
//初始化第一个组合,输出
//a[0]=-1;
for(int i=1; i<=r; i++) a[i]=i;
print(a, r);
//按照字典序递增
while(next_combination(a, n, r)) { //求取下一组合,判断是否溢出
print(a, r); //输出当前组合,即递增后的组合
}
}
return 0;
}