回溯与递归
递归:递归算法的实质是把问题分解成规模缩小的同类问题的子问题(分治),然后递归调用方法来表示问题的解。
回溯:按选优条件向前搜索,以达到目标。但当探索到某一步时,发现原先选择并不优或达不到目标,就退回一步重新选择,这种走不通就退回再走的技术为回溯法。通过剪枝可以大幅减少解决问题的计算量。
全排列(1~n)
给出数字n,打印1~n的所有可能排列。
基本思路
设置数组P存储所求排列,hashTable数组作为标记数组,标记数字是否已加入排列。假设当前已经填好了P[1]-P[index-1],正准备填P[index]。于是枚举1-n,如果当前枚举到的数字x不在P[1]~P[index-1]中,那么把它填入P[index],然后处理P的第index+1位(递归)。当index = n + 1时,显然P[1]-P[n]已填好,进入递归边界,进行输出。
这里设n=3,代码如下:
#include <cstdio>
const int maxn = 11;
int n, P[maxn], hashTable[maxn] = {false};
void generateP(int index){ //处理第index号位
if(index == n + 1){ //已经处理完1~n位
for(int i = 1; i <= n; i++){
printf("%d", P[i]);
}
printf("\n");
return ;
}
for(int x = 1; x <= n; x++){ //试图将x填入P[index]
if(hashTable[x] == false){ //x不在P中
P[index] = x;
hashTable[x] = true;
generateP(index + 1); //进入递归下一层
hashTable[x] = false; //已处理完P[index]为x的子问题
}
}
}
int main(){
n = 3;
generateP(1);
return 0;
}
n皇后
n*n棋盘,放置n个皇后,要求任意两个皇后不在同一列、同一行、同一对角线上,求出方案数。
基本思路
可把n列皇后所在的行号依次写出,用全排列思想解决,每生成一个排列判断是否有处在同一条对角线上的皇后即可(同行同列显然不可能),没有则符合条件。但全排列需要n!次枚举,而在八皇后问题中,放置了一部分皇后时,剩余的皇后无论怎样放置都不合法,此时就没必要往下递归,直接返回上层即可。因此,以下采用回溯法,在摆第index个皇后时,判断其是否与先前的皇后冲突,若冲突,则返回上层。
代码
void generateP(int index){
if(index == n + 1){
count++;
return;
}
for(int x = 1; x <= n; x++){ //枚举可能的行号
if(hashTable[x] == false){ //该行没有皇后
bool flag = true;//flag为true表示当前皇后不会与之前的冲突
for(int pre = 1; pre < index; pre++){//遍历之前的皇后
if(abs(index - pre) == abs(x - P[pre])){ //与之前的皇后在同一条对角线
flag = false;
break; //返回上一层
}
}
if(flag){
P[index] = x;
hashTable[x] = true;
generateP(index + 1);
hashTable[x] = false;
}
}
}
}
2n皇后
n* n棋盘,对应n* n个整数,若整数为1,表示可放皇后,整数不为1,表示不可放皇后。放置n个白皇后,n个黑皇后,使任意两个黑皇后不在同一行、同一列、同一对角线上,白皇后同理。求方案数。
基本思路
先放置白皇后,再放置黑皇后,其余思想与n皇后一致,只是在枚举每列行号的时候判断条件增多,具体见代码。
代码
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
int black[10], white[10];
int m[10][10]; //地图
int n, cnt = 0;
bool hash_B[10] = {false}, hash_W[10] = {false};
bool judge(int P[], int index, int x){
//判断当前摆放皇后是否与先前的冲突
for(int pre = 1; pre < index; pre++){
//index列皇后行号为x,pre列皇后行号为P[pre]
if(abs(index - pre) == abs(x - P[pre]))
return false;
}
return true;
}
void generateB(int index){ //放置黑皇后
if(index == n + 1){
cnt++;
return;
}
for(int x = 1; x <= n; x++){
//不冲突 && 可放置皇后 && 该位置没有白皇后 && 该行没有黑皇后
if(judge(black, index, x) && m[x][index] == 1
&& x != white[index] && hash_B[x] == false){
black[index] = x;
hash_B[x] = true;
generateB(index + 1);
hash_B[x] = false;
}
}
}
void generateW(int index){ //放置白皇后
if(index == n + 1){
//放置完白皇后,开始放置黑皇后
generateB(1);
return;
}
for(int x = 1; x <= n; x++){
//不冲突 && 可放置皇后 && 该行没有白皇后
if(judge(white, index, x) && m[x][index] == 1
&& hash_W[x] == false){
white[index] = x;
hash_W[x] = true;
generateW(index + 1);
hash_W[x] = false;
}
}
}
int main(){
cin>>n;
for(int i = 1; i <= n; i++)
for(int j = 1; j <= n; j++)
cin>>m[i][j];
generateW(1);
cout<<cnt<<endl;
return 0;
}