使用剪枝函数的深度优先生产状态空间树中结点的求解方法称为回溯法。
- 算法搜索至任一结点时,先判断以该结点为根的子树是否包含问题的解。如果肯定不包含,则跳过对该结点为根的子树的搜索,逐层向其父亲结点回溯;否则,进入该子树,继续按深度优先策略搜索。
- 为提高搜索效率,在搜索过程中用约束函数和限界函数(统称剪枝函数)来剪去不必要搜索的子树,减少问题求解所需实际生成的状态空间树结点数,从而避免无效搜索。
n-皇后
问题:寻找在棋盘(nXn)上放置这n个皇后的方案,使得他们中的任何两个都不在同一行、同一列或同一斜线上。
- 显示约束:1)xi=0,1,2,…,n-1 ;2)不同列:xi≠xj(i≠j)
- 隐式约束:不处于同一正、反对角线:|i−j|≠|xi−xj|(i≠j)
- xi=k表示第i+1个皇后放置在第k列
对应解空间为n!
typedef long long int ll; int N,cot;///N分别代表棋盘尺寸(皇后数量)和皇后可能成功摆放的结果总数 int size[11];//数组的下标表示行,对应的值表示列,N<=10 int ans[11]; // 用于打表存放结果 bool Place(int k,int i) { for(int j=1;j<k;j++)///j从第一行开始 { if(size[j]==i||((abs(j-k))==abs(size[j]-i))) return false; } return true; } void dfs(int k)///k表示在第几行 { for(int i=1;i<=N;i++)///i表示在第几列 { if(Place(k,i)) { size[k]=i; ///代表将第一个皇后放置在第1行第i列(或者说第n个皇后) if(k==N+1)///当最后一个皇后成功放置时返回. { cot++;///成功的次数加一 return ; } else dfs(k+1);///如果放置在第i列不冲突,则开始放下一个皇后 } } } int main() { for( N=1;N<=11;N++)///因为本题时间限制,所以需要先打表 { cot=0;///临时存放结果 dfs(1);///从第一行开始放置 ans[N]=cot;///将结果依次放入表中 } while(cin >>N) { if(!N) break; cout <<ans[N]<<endl; } return 0; }
子集和问题
给定一个含有n个元素的整形数组,再给定一个和sum,求出数组中满足给定和的所有元素组合存在一个数组中,求满足的集合有几个。
对于数组中任意一个元素,先将其放入结果集中,如果当前和不超出给定和,那就继续考察下一个元素,如果超出给定和,则舍弃当前元素。如此往复,直到找到所有可行解。
const int maxn=1000; int w[maxn];///存储所有元素 bool vis[maxn];///判断该元素是否被加入子集 int M,n;///M是指定的和,n是包含的整数个数 void SUMOFSUB(int s, int k, int r) {///s是已选择的元素和,k是即将选择的元素下标,r是剩余元素和 int j; vis[k] = true; if (s + w[k] == M) { for (j = 1; j <= k; j++) { if(vis[k]) cout << w[k]<< " "; } cout << endl; } else if (s + w[k] + w[k + 1] <= M) { SUMOFSUB(s + w[k], k + 1, r - w[k]);///搜索左子树 } if ((s + r - w[k] >= M) && (s + w[k + 1] <= M))///搜索右子树 { vis[k]=false;///回溯时先还原 SUMOFSUB(s, k + 1, r - w[k]); } } int main() { int i,r; w[0] = 0; cin>>n>>M; for (i=1,r=0;i <= n;i++) { cin>>w[i]; r += w[i]; } memset(vis,false,sizeof(vis)); SUMOFSUB(0, 1, r); return 0; }