指数枚举,排列枚举,组合枚举以及n皇后问题
指数形枚举:即集合中的元素,选或者不选两种状态,$0$和$1$, 即集合的所有子集。例如$\left \{ 1,2 \right \}$集合中的子集有$\left \{ \right \},\left \{ 1 \right \},\left \{ 2 \right \},\left \{ 1,2 \right \}$。
运用深搜进行指数型枚举,在挑选数字时,用一个状态$state$记录挑选的数字,从前往后枚举,当枚举到最后一位的后一位
的时候,输出。
1 #include <iostream> 2 3 using namespace std; 4 5 const int N = 20; 6 7 int a[N]; 8 int n; 9 10 void dfs(int step, int state) 11 { 12 if(step == n)//当枚举到最后一位的后一位,即代表前面n位都尝试过,输出 13 { 14 for(int i = 0 ; i < n ; i ++)//遍历状态 15 if(state >> i & 1)//如果该数字的状态是选中就输出 16 cout << i + 1 << " ";//由于step从0开始,数字从1开始,要加上偏移量 17 cout << endl; 18 return;//输出完务必return 19 } 20 dfs(step + 1, state);//对下一个数做决定,不选当前数 21 dfs(step + 1, 1 << step | state);//对下一个数做决定,选当前数 22 } 23 24 int main(){ 25 cin >> n; 26 27 dfs(0, 0); 28 29 return 0; 30 }
组合型枚举:组合型枚举一般为从$n$个数中选择$m$个数,$m\leq n$。例如$\left \{ 1,2 ,3\right \}$中选$2$个数,有$\left \{ 1,2 \right \},\left \{ 1,3 \right \},\left \{ 2,3 \right \}$。
依旧是运用深搜枚举,同样用一个$state$记录被选数字,与指数型枚举不同的是:1.必须优先选择当前数字。2.当已经到最后一个数字的后一位时还没有选择满$m$个数字,要$return$。
1 #include <iostream> 2 3 using namespace std; 4 5 const int N = 25; 6 7 int a[N]; 8 int n, m; 9 10 void dfs(int step, int state, int cnt) 11 { 12 if(cnt == m)//当选满m个数字,输出数字 13 { 14 for(int i = 0 ; i < n ; i ++) 15 if(state >> i & 1) 16 cout << i + 1 << " "; 17 cout << endl; 18 return;//记得return 19 } 20 if(step == n)return;//如果到最后一位后一位还未满m个数字,return 21 dfs(step + 1, 1 << step | state, cnt + 1);//优先选择当前数字的方案 22 dfs(step + 1, state, cnt);//不选择当前数字的方案 23 24 } 25 26 int main(){ 27 cin >> n >> m; 28 29 dfs(0, 0, 0); 30 return 0; 31 }
排列型枚举:即$\left \{ 1,2 \right \}$与$\left \{ 2,1 \right \}$是不同的排列方案。
用深搜进行搜索,用一个$st$数组标记被选数字,用$a$数组存储被选数字。
1 #include <iostream> 2 3 using namespace std; 4 5 const int N = 10; 6 7 int a[N]; 8 bool st[N]; 9 int n; 10 11 void dfs(int step) 12 { 13 if(step == n) 14 { 15 for(int i = 0 ; i < n ; i ++) 16 cout << a[i] << " "; 17 cout << endl; 18 return; 19 } 20 for(int i = 1 ; i <= n ; i ++)//枚举1-9的数字 21 if(!st[i])//如果没有用过 22 { 23 a[step] = i;//将该数组存储到a数组内 24 st[i] = true;//标记该数字已经选过 25 dfs(step + 1);//进行下一步 26 st[i] = false;//回溯 27 } 28 } 29 30 int main(){ 31 cin >> n; 32 33 dfs(0); 34 35 return 0; 36 }
$n$-皇后问题:$n$-皇后问题是指将 $n$ 个皇后放在 $n∗n$ 的国际象棋棋盘上,使得皇后不能相互攻击到,即任意两个皇后都不能处于同一行、同一列或同一斜线上。
我们使用深搜,用$step$代表一次步骤,即代表一行,对于每一行,枚举每一列,判断在该位置上的竖直方向,两个斜边方向上是否有皇后,没有就放下。
判断竖线上是否有皇后用一个$st$数组即可,对于斜边,我们知道,对于处在同一条斜边上的(列数-行数)和 (列数+ 行数)的数值是相同的。

例如对于(列数-行数)的值如是。
1 #include <iostream> 2 3 using namespace std; 4 5 char g[20][20]; 6 bool st[20], a[20], b[20]; 7 int n; 8 9 void dfs(int step) 10 { 11 if(step == n) 12 { 13 for(int i = 0 ; i < n ; i ++)cout << g[i] << endl; 14 puts(" "); 15 return; 16 } 17 for(int i = 0 ; i < n ; i ++) 18 if(!st[i] && !a[i + step] && !b[i - step + n])//竖直方向和两斜边都没有皇后可以攻击到 19 { 20 g[step][i] = 'Q';//放下 21 st[i] = a[i + step] = b[i - step + n] = true; 22 dfs(step + 1); 23 g[step][i] = '.';//回溯 24 st[i] = a[i + step] = b[i - step + n] = false; 25 } 26 } 27 28 int main(){ 29 cin >> n; 30 for(int i = 0 ; i < n ; i ++) 31 for(int j = 0 ; j < n ; j ++)g[i][j] = '.'; 32 33 dfs(0); 34 35 return 0; 36 }

浙公网安备 33010602011771号