题目描述
排列与组合是常用的数学方法,其中组合就是从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 )。
输出
所有的组合,每一个组合占一行且其中的元素按由小到大的顺序排列,所有的组合也按字典顺序。
参考链接:
https://blog.csdn.net/qq_20679687/article/details/89411791
分析:题目要求使用非递归的方法输入;
注意到,组合的输出,是当前未被输出的最小的序列,然后从最后面一位开始自增到n。然后再找到此时未被输出的最小序列,重复操作。
具体分析可以看代码。
void print(int a[],int r)
{
for(int i=1;i<=r;i++)
cout<<a[i]<<" ";
cout<<endl;
}
bool handle(int a[],int r,int n)
{
if(a[r]<n)
a[r]++; //从最后一个自增到n
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;
cin>>n>>r;
int a[100];
for(int i=1;i<=r;i++)
a[i]=i; //先给一个最小的序列
print(a,r);
while(handle(a,r,n)) //处理
print(a,r);
return 0;
}
个人觉得关键代码
int i=1;
while(a[r-i]==n-i&&r-i>0) //找到改变的位置
i++;这里这样理解,因为序列是按小到大,那么 r-i 意味着从最大位开始递减 ,n-i是该位可以接受的最大的数字。
那么如果相等,该位就不能变换,只能在往下找。