- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
例 0/1背包问题
•M,n;wi, pi •(x0,x1,x2,x3) •显示约束:xi∈{0,1} •隐式约束: ∑ xiwi≤M •目标函数: ∑ xipi •当n=3时对应的状态空间
第1 层 第2 层 第3 层 0 1 0
树
0
1
1 0 1 0 1 1 0 1
•树中总结点数2n+1-1 •树中叶子结点数2n
约束函数:对所有 i 和 j , 0i,j<k , ij ,若 a[i][j]=1 , 则xixj。
8.4.3 图着色算法
【程序8-6】 图的m着色算法 template <class T> void MGraph<T>::NextValue(int k,int m,int *x) { //确定分量x[k]的取值。[1,m]。x[k]初始值=0 do { x[k]=(x[k]+1) % (m+1); //尝试下一种颜色 if (!x[k]) return; //没有可用颜色 for (int j=0; j<k; j++) //可行判定 if (a[k][j] && x[k] == x[j]) break; if (j==k) return; //成功 } while (1); }
如排序问题相同,皇后问题的状态空间树是一棵 排列树。排列树有n!个叶结点,遍历排列树的时间 为(n!)。
4皇后问题对应的排列图
8.2.3 n-皇后算法
【程序8-4】 n-皇后问题的回溯算法 bool Place(int k, int i,int* x) //xk=i是否可行 {//判定两个皇后是否在同一列或在同一斜线上 for (int j=0;j<k;j++) if ((x[j]==i) || (abs(x[j]-i)==abs(j-k))) return false; return true; } void NQueens(int n,int *x) { NQueens(0,n,x); }
目标函数,也称代价函数(cost function),用来 衡量每个可行解的优劣。使目标函数取最大(或最 小)值的可行解为问题的最优解。 状态空间树(state space)是描述问题解空间的树 形结构。树中每个结点称为一个问题状态 (problem state)。如果从根到树中某个状态的路 径代表了一个作为候选解的元组,则称该状态为解 状态( solution state)。若这个候选解可行,则称 该解状态为答案状态(answer state)。
s wi xi , r wi
i 0 i k
k 1
n 1
s—部分和 r—剩余和
xk=1
xk=0
s+r≥M && s+wk≤M s+r-wk≥M
8.3.3 子集和数算法
•【程序8-5】子集和数的回溯算法
void SumOfSub (float s, int k, float r, int* x, float m, float* w) //s—部分和 r—剩余和 { x[k]=1; if (s+w[k]==m) { //得到一个可行解 for (int j=0;j<=k;j++) cout<<x[j]<< ' '; cout<<endl; }
例 8- 3 设有n=6 个正数的集合 W=(5,10,12,13,15,18)和整数 M=30,求W的所有元素之和为M的子集。
8.4 图的着色
8.4.1 问题描述
已知无向图 G=(V,E) 和 m 种不同的颜色,如果只允 许使用这m种颜色对图G的结点着色,每个结点着一 种颜色。问是否存在一种着色方案,使得图中任意 相邻的两个结点都有不同的颜色。这就是 m- 着色判 定问题(m-colorability decision problem)
部分向量(x[0],,x[k-1]) 代表从根到一个中间状态Y的路径 T(x[0],,x[k-1]) 代表xk所有可能取值 约束函数 Bk(x[0],,x[k])
【程序8-2】 迭代回溯法 Void IBacktrack(int n) { int k=0; while (k>=0){ if (还剩下尚未检测的x[k],使得x[k] T(x[0],,x[k-1]) && Bk(x[0],,x[k]){ if ( (x[0],x[1],,x[k])是一个可行解) 输出(x[0],x[1],,x[k]); k++; } else k--; } }
void NQueens(int k,int n,int *x) { for (int i=0; i<n;i++) { if (Place(k,i,x)) { x[k]=i; if (k==n-1) { for(i=0;i<n;i++) cout<<x[i]<<" "; cout<<endl; } else NQueens(k+1,n,x); } } }
第4 层
0
例 排序问题
•元素a0,a1,…,an-1排序问题 •解向量(x0,x1,…,xn-1), xi表示 元素ai在排列中的位置 •显示约束:xi∈{0,1,…,n-1} 且xi≠xj (i≠j) •隐式约束: ai≤aj (xi≤xj) •无目标函数 •(13,24,09) •树中总结点数:1+n+n(n1)+n(n-1)(n-2)+…+n! •树中叶子结点数n!
4皇后问题的两个可行解
其中一个可行解的回溯过程
X XX X
X X
实际生成的树结点
8.2.4 时间分析
可通过蒙特卡罗法计算 5 次试验中实际需要生成 的结点数的平均值为1625。8-皇后问题状态空间树 的结点总数是:
1 ( 8 i ) 109601
j 0 i 0 7 j
因此,需要实际生成的结点数目大约占总结点数 的1.55%。
0 1 2 … n-1
ai
8.1.2剪枝函数和回溯法
为了提高搜索效率,在搜索过程中使用约束函数 (constraint function),可以避免搜索那些已知不 含答案状态的子树。 如果是最优化问题,还可使用限界函数( bound function )剪去那些不可能包含最优答案结点的子 树。约束函数和限界函数的目的都是为了剪去不必 要搜索的子树,减少问题求解过程中实际生成的状 态 结 点 数 , 它 们 统 称 为 剪 枝 函 数 ( pruning function)。
函数size返回集合S的大小;函数Choose从集合S中随机选择一个
8.2 n-皇后
8.2.1 问题描述
n-皇后问题要求在一个nn的棋盘上放置n个皇后, 使得它们彼此不受“攻击”。 n- 皇后问题要求寻找 在棋盘上放置这n个皇后的方案,使得它们中任何两
个都不在同一行、同一列或同一斜线上。
8.2.2 回溯法求解
第2部分 算法设计策略 第8章 回溯法
8.1 8.2 8.3 8.4 8.5 8.6 8.7
一般方法 n-皇后 子集和数 图的着色 哈密顿环 0/1背包 批处理作业调度
8.1 一般方法
1. 问题的所有可能的解可以用树形
结构来表示, 2. 通过在树中遍历搜索找到可行解 或最优解
8.1.1 基本概念
8.3 子集和数
8.3.1 问题描述
•已知 n 个不同正数 wi , 0in-1 ,的集合,求该集合
的所有满足条件的子集,使得每个子集中的正数之 和等于另一个给定的正数M。 例 8- 2 设有n=4个正数的集合 W={w0,w1,w2,w3}=(11,13,24,7) 和整数 M=31 ,求 W 的 所有满足条件的子集,使得子集中的正数之和等于M。
使用剪枝函数的深度优先生成状态空间树中结点 的求解方法称为回溯法(backtracking); 广度优先生成结点,并使用剪枝函数的方法称为 分枝限界法(branch-and-bound)。
【程序8-1】递归回溯法 Void RBacktrack(int k) { //应以Rbacktrack(0)调用本函数 for (每个x[k],使得 x[k]T(x[0],,x[k-1]) &&(Bk(x[0],,x[k])){ if ( (x[0],x[1],,x[k])是一个可行解) 输出 (x[0],x[1],,x[k]); RBacktrack(k+1); } }
【程序8-3】 蒙特卡罗算法 int Estimate(SType* x) { int k=0, m=1, r=1; do { SetType S={ x[k]| x[k]T(x[1],,x[k1]) && Bk(x[1],,x[k]==true)}; if (!Size(S)) return m; r=r*Size(S); m=m+r; x[k]=Choose(S);k++; }while(1); }
•如:地图着色问题 、四色猜想
8.4.2 回溯法求解
设无向图 G=(V,E) 采用如下定义的邻接矩阵 a 表示:
1 a [ i ][ j ] 0 如果( i, j ) E 其它
解的形式:n-元组(x0,x1,,xn-1), xi{1,…,m}, 0i<n,表示结点i的颜色,这就是 显式约束。 xi=0 表示没有可用的颜色。因此解空间 的大小为mn。 隐式约束可描述为:如果边(i,j)E,则 xixj。
else if (s+w[k]<m) //搜索左子树 SumOfSub(sf ( (s+r-w[k]>=m) ) { //搜索右子树 x[k]=0; SumOfSub(s, k+1, r-w[k], x, m, w); } } void SumOfSub (int* x, int n, float m, float* w) { float r=0; for(int i=0;i<n;i++) r=r+w[i]; //初时化r, 应有r≥m if(r>=m && w[0]<=m) SumOfSub(0, 0, r, x, m, w); }