題目描述
排列與組合是常用的數學方法,其中組合就是從n個元素中抽出r個元素(不分順序且r ≤n),我們可以簡單地將n個元素理解爲自然數1,2,…,n,從中任取r個數。
現要求你輸出所有組合。
例如n=5,r=3,所有組合爲:
12 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)。
輸出格式
所有的組合,每一個組合佔一行且其中的元素按由小到大的順序排列,每個元素佔三個字符的位置,所有的組合也按字典順序。
**注意哦!輸出時,每個數字需要33個場寬,pascal可以這樣:
write(ans:3);
輸入輸出樣例
輸入 #1
5 3
輸出 #1
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
解決方法:
當然可以用遞歸解決,還有一種方法是用二進制的性質解決。
分析例子:
由輸入 5 3得到的輸出結果我們可以知道可以用11100代表123等以此類推。(方便理解的方式是這樣,用一個bool 型數組 data[5] 存儲位置是否輸出,輸出內容爲下標+1)。
由給出的輸出可以看出對應的二進制數爲:
1 2 3 -> 11100
1 2 5 -> 11001
1 3 4 -> 10110
1 3 5 -> 10101
1 4 5 -> 10011
2 3 4 -> 01110
2 3 5 -> 01101
2 4 5 -> 01011
3 4 5 -> 00111
上代碼: 分析了本題輸出對應的二進制,那麼主要問題就是如何得到這個二進制->先看代碼,根據代碼慢慢解釋。
void work_show(int n,int r)
{
int max = 1 << n;
for(int x = max-1;x >= 0;x--){ //1 -> 見代碼下面的解釋,下同
int num = 0;
for(int i = n-1;i >= 0;i--) //2
if((x>>i)&1)
num++;
if(num == r){
for(int i = n-1;i >= 0;i--) //3
if((x>>i)&1)
printf("%3d",n-i);
printf("\n");
}
}
}
在解釋程序之前先說一下整體思路: 因爲00111、01011、···、10101、···、11100屬於00000、00001、00010、···、11110、11111即 0 ~ 31,即 0 ~ 25-1(25 = 1 << 5),所以思路就是從0 ~ 2^5-1 的數中找出二進制符合1的個數等於3的,並按序輸出。因爲輸出順序是按照11100~00111的順序來的,所以對應的整數應該倒序,即對應代碼中的 (x = max -1; x >= 0;x–)
- 從2^n-1 ~ 0倒序遍歷,num 記錄當下 x 對應二進制中所含的 1 的個數。
- 數出x對應二進制中所含 1 的個數,爲了解釋清楚舉個例子:11100,因爲奇數即末尾爲1的數 & 1 等於 1,相反偶數即末尾爲 0 的數 & 1等於 0。所以要判斷 11100 的最高位爲多少可以讓其右移4(即 n - 1)位之後 & 1,若等於 1 即該位爲 1,相反則爲 0。同理,要判斷第二高位是否爲1可以讓其右移 3 位,所以 i 代表右移幾位按 n-1 ~ 0 遍歷。
- 若此時 x 對應的二進制數所含的 1 個數爲 3,此時按該二進制數代表的序列輸出。輸出方式同 2 中的循環。