實現思路就是對於當前第u位數(u <= n),分爲選與不選兩種情況,構成了一棵二叉選擇樹,最終的葉節點就是答案。
爲了節省空間和簡化操作,使用狀態壓縮,對於state,第i位爲1表示i這個數被選中。
#include<iostream>
using namespace std;
int n;
void dfs(int u, int state) {//u表示判斷進度,表示對第u個數(從0開始)進行選擇
if (u == n) {
for (int i = 0; i < n; i++) {
if (state >> i & 1) {
cout << i + 1 << " ";
}
}
cout << endl;
return;
}
dfs(u + 1, state);// 分支:不用這個數
dfs(u + 1, state | 1 << u);//用這個數
}
int main() {
cin >> n;
dfs(0, 0);
return 0;
}
下面看一道例題:
#include <iostream>
using namespace std;
int n,m;
void dfs(int u , int sum, int state){//u表示對第u個數(從0開始)進行選擇,sum表示已選的個數
if (sum + n - u < m) return;
if (sum == m){//此時的邊界不再是n == u,只要滿足了選擇個數就要退出
for (int i = 0; i < n; i++){
if(state >> i & 1){
cout << i + 1 << " ";
}
}
cout << endl;
return;
}
dfs(u + 1, sum + 1, state | 1 << u );
dfs(u + 1, sum, state);
}
int main(){
cin >> n >> m;
dfs(0,0,0);
return 0;
}
二、排列問題
排列問題是選了再排,所以既要保證元素唯一性又要保證順序唯一性。state狀態壓縮只能保證元素的唯一性,而無法保證順序性。所以需要額外開闢數組記錄每一次的選擇狀態。
主要思想如下:首先將選取的位置看做n個坑,對於第u個坑(u從0開始)需要考慮選哪個數來填坑(而不再是考慮填與不填這種哲學問題了)。所以能填坑的就是那些還未被選擇的那些數,每選一個數就是一種可能,要用數組記錄下來。如此循環,等第n個坑也被填完的時候就是一種結果。
#include <iostream>
#include <vector>
using namespace std;
int n;
vector<int> path;
void dfs(int u, int state){//對第u個坑,放哪些數, 用state保證每個數只被選一次
if (u == n){
for(auto x : path) cout << x << " ";
cout << endl;
return;
}
for(int i = 0; i < n; i ++){
if ( !(state >> i & 1) ){
path.push_back(i + 1);//在第u個坑上填i + 1
dfs(u + 1, state | 1 << i);
path.pop_back();//擦屁股
}
}
}
int main(){
cin >> n;
dfs(0,0);
return 0;
}