從n個數中選取m個數的所有組合

題目:

在這裏插入圖片描述

n個數1,2,...,n,從這n個數中任意選m個數,輸出所有不同組合,共有C(n,m)種不同組合。

如n=4,m=2,會產生如下輸出:
1 2 
1 3 
2 3 
1 4 
2 4 
3 4
 
如n=5,m=3,會產生如下輸出:
1 2 3 
1 2 4 
1 3 4 
2 3 4 
1 2 5 
1 3 5 
2 3 5 
1 4 5 
2 4 5 
3 4 5 

題解:

1. 題解一:(正向打印)

1. 選擇第i(m <=i<=n)個元素作爲每個組合的最後元素,在第1————i個元素中往前(前i - 1個元素中)選取m-1個元素。
2. 若m等於1(對應b[0]),則表示選完,輸出該組合(數組b中存儲的是組合的元素在a中的下標)
3. 若m>1,則重複12步驟

例如:

從後往前選取,選定位置i後,再在前i-1個裏面選取m-1個。

如 1 2 3 4 5 中選取 31、如果不包含5、也不包含4,直接選取3,那麼再在前2個裏面選取2個,剛好只有兩個。

2、如果只不包含5,直接選定4,那麼再在前3個裏面選取2個,而前3個裏面選取2個又是一個子問題,遞歸即可。

3、選取5後,再在前4個裏面選取2個,而前4個裏面選取2個又是一個子問題,遞歸即可。

縱向看,123剛好是一個for循環,初值爲m(m == 3),終值爲n(n == 5)

橫向看,該問題爲一個前i-1箇中選m-1的遞歸。

2. 題解二:(反向打印)

組合問題就是從n中選m個數,也是採用遞歸的方式

a. 首先從n個數中選取編號最大的數,然後在剩下的n-1個數裏面選取m-1個數,直到從n-(m-1)個數中選取1個數爲止。

b. 從n個數中選取編號次小的一個數,繼續執行1步,直到當前可選編號最大的數爲m。

代碼:

1. 代碼一:(正向打印)

import java.util.*;

public class Main {

    public static void C(int n, int m, int a[], int b[])
    {
        for(int i = m; i <= n; i++)
        {
            b[m - 1] = i - 1;
            if(m > 1)
            {
                C(i - 1, m - 1, a, b);
            }
            else
            {
                for(int j = 0; j < b.length; j++)
                {
                    System.out.printf("%d ", a[b[j]]);
                }
                System.out.println();
            }
        }
    }

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt(); // 4
        int m = sc.nextInt(); // 2
        int a[] = new int[n];
        for(int i = 0; i < n; i++)
        {
            a[i] = i + 1; // a[0, 1, 2, 3] = { 1, 2, 3, 4};
        }
        int b[] = new int[m]; // 數組b中存儲的是組合的元素在a中的下標
        C(n, m, a, b);
        sc.close();
    }
    
}

// // 輸入:
// 4 2

// // 輸出:
// 12
// 13
// 23
// 14
// 24
// 34

2. 代碼二:(反向打印)

/// 求從數組a[1..n]中任選m個元素的所有組合。
/// a[1..n]表示候選集,n爲候選集大小,n>=m>0。
/// b[1..M]用來存儲當前組合中的元素(這裏存儲的是元素下標),
/// 常量M表示滿足條件的一個組合中元素的個數,M=m,這兩個參數僅用來輸出結果。
void combine( int a[], int n, int m,  int b[], const int M )
{
  for(int i=n; i>=m; i--)   // 注意這裏的循環範圍
  {
     b[m-1] = i - 1;
     if (m > 1)
       combine(a,i-1,m-1,b,M);
     else                     // m == 1, 輸出一個組合
     {
       for(int j=M-1; j>=0; j--)
       cout << a[b[j]] << " ";
       cout << endl;
     }
  }
}

參考:

  1. (劍指offer)從n個數中選取m個數的所有組合
  2. 打印從n個數種選取m個數的組合數
  3. 排列組合算法
  4. C語言實現的排列組合問題的通用算法、解決方法
  5. 輸出從n個數中選m個數的所有組合
  6. 遞歸實現 從n個數中選取m個數的所有組合
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章