題目描述
排列與組合是常用的數學方法,其中組合就是從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是該位可以接受的最大的數字。
那麼如果相等,該位就不能變換,只能在往下找。