洛谷P1157 組合的輸出-二進制

題目描述

排列與組合是常用的數學方法,其中組合就是從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–)

  1. 從2^n-1 ~ 0倒序遍歷,num 記錄當下 x 對應二進制中所含的 1 的個數。
  2. 數出x對應二進制中所含 1 的個數,爲了解釋清楚舉個例子:11100,因爲奇數即末尾爲1的數 & 1 等於 1,相反偶數即末尾爲 0 的數 & 1等於 0。所以要判斷 11100 的最高位爲多少可以讓其右移4(即 n - 1)位之後 & 1,若等於 1 即該位爲 1,相反則爲 0。同理,要判斷第二高位是否爲1可以讓其右移 3 位,所以 i 代表右移幾位按 n-1 ~ 0 遍歷。
  3. 若此時 x 對應的二進制數所含的 1 個數爲 3,此時按該二進制數代表的序列輸出。輸出方式同 2 中的循環。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章