2020-wfx-第5章 回溯法-应用
合集下载
相关主题
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
swap(x[i],x[j]); //恢复状态
…
//第i层的结点选择x[j]的恢复操作
}
}
}
void dfs(int a[],int n,int i) {
if (i>=n) displaySolution(a,n);
else {
for (int j=i;j<n;j++) {
swap(a[i],a[j]);
dfs(a,n,i+1);
swap(a[i],a[j]); } } }
//求a[0..n-1]的全排列 //递归出口
//交换a[i]与a[j] //交换a[i]与a[j]:恢复
void swap(int &x,int &y) {
int tmp=x; x=y; y=tmp }
//交换x,y
void displaySolution(int a[],int n) //输出一个解 {
取3
取2
取3
取1
取1
取2
{1,2,3} {1,3,2} {2,1,3} {2,3,1} {3,2,1} {3,1,2}
第0层
第1层
第2层 第3层
(叶子结点)
设f(a,n,i)表示求解a[i..n-1]子序列(共n-i个元素)的全排 列(0≤i≤n-1),排列为aiai+1…an-1。
f(a,n,i+1)表示求解a[i+1..n-1]子序列(共n-i-1个元素) 的全排列排列为ai+1ai+2…an-1。
f(a,n,i) 输出s所有元素即产生一种全排列 若i=n
问题求解
f(a,n,i):大问题
a0 a1 … ai
ai+1 …
an-1
f(a,n,i+1):小问题
f(a,n,i) for (j=i;i<n;j++)
{
f(a,n,i+1);
}
其他情况
问题求解
a0 a1
a[i]取a[i] a[i]取a[i+1] ... a[i]取a[n-1]
{} {3} {2} {2,3} {1} {1,3} {1,2}{1,2,3}
dfs(0,[0,0,0])
0
1
dfs(1,[0,0,0])
0
1
dfs(2,[0,0,0])
0
1
dfs(2,[0,1,0])
01
dfs(1,[1,0,0])
0
1
dfs(2,[1,0,0])
01
dfs(2,(1,1,0])
3.确定a[2] 取3
取2
取3
取1
取1
取2
a={1,2,3}a={1,3,2}a={2,1,3}a={2,3,1} a={3,2,1}a={3,1,2}
解空间-排列树
{1,2,3}
取1
取2
取3
{1,2,3}
取2
取3
{2,1,3}
取1
取3
{3,2,1}
取2
取1
{1,2,3} {1,3,2} {2,1,3} {2,3,1} {3,2,1} {3,1,2}
f(a,n,i):大问题
f(a,n,i+1):小问题
… ai
ai+1 …
an-1
交换a[i],a[i] 交换a[i],a[i+1]
... 交换a[i],a[n-1]
调用 f(a,n,i+1)
交换a[i],a[i] 交换a[i],a[i+1] ... 交换a[i],a[n-1]
递归模型
f(a,n,i) 输出s所有元素即产生一种全排列 若i=n
问题
设计一个算法在1,2,…,9(顺序不能变)数字之间插入+或-或什么都 不插入,使得计算结果总是100的程序,并输出所有的可能性。
例如:1+2+34–5+67–8+9=100
数码:1 2 3 4 5 6 7 8 9 运算符:‘+’‘–’‘ ’
表达式: 1+2+3 4-5+6 7-8+9 a[0..9] :1 2 3 4 5 6 7 8 9 op[1..9]: + + B - + B - +
交换 a[2],a[2]
交换 a[2],a[2]
交换 a[2],a[2]
交换 a[2],a[2]
交换 a[2],a[2]
{3,1,2}
交换 a[2],a[2]
{1,2,3} {1,3,2} {2,1,3} {2,3,1} {3,2,1} {3,1,2}
算法设计与分析
吴粉侠
第5章 回溯法
回溯法应用
{ x[i]=0; dfs(a,n,i+1,x);//不选择a[i]
x[i]=1; dfs(a,n,i+1,x); //选择a[i]
dfs(a,n,i,x)简写为dfs(i,x) dfs(a,3,0,x)简写为dfs(0,x)
dfs(0,[0,0,0])
} }
dfs(0,[0,0,0])
0
1
dfs(1,[0,0,0])
算法设计与分析
吴粉侠
第5章 回溯法
回溯法应用
幂集求解
【问题描述】对于给定的正整数n(n≥1),求1~n构成的集合的
所有子集(幂集)。
{}
a={1 , 2 , 3}
添加1 {1}
1的幂集:{{ },{1}} 添加2
{{ 2 },{1,2}}
1~2的幂集:{ { }, {1},{ 2 },{1,2}}
cout<<“{“; for(int i=0;i<n;i++) {
if(x[i]==1) cout<<‘\t’<<a[i]; } cout<<“}“; }
void dfs(int a[],int n,int i,int x[])
{ if (i>=n)
dispasolution(a,n,x);
else
交换 a[0],a[2]
}
dfs
dfs
dfs
}
({1,2,3},3,1) ({2,1,3},3,1)
({3,2,1},3,1)
}
交换a[1],a[1]
交换a[1],a[2] 交换a[1],a[1]
交换a[1],a[2] 交换a[1],a[1] 交换a[1],a[2]
dfs
dfs
dfs
dfs
dfs
dfs
cout<<“(“; for(int i=0;i<n-1;i++) { cout<<a[i]<<‘,‘; } cout<<a[n-1]<<“)“; }
void dfs(int a[],int n,int i)
{ if (i>=n) displaySolution(a,n);
else
{ for (int j=i;j<n;j++)
态是(i=n,x为一个解)。 从状态(i,x)可以扩展出两个状态:
不选择a[i]元素 下一个状态为(i+1,x[i]=0) 选择a[i]元素 下一个状态为(i+1,x[i]=1)
算法设计
void dfs(int a[],int n,int i,int x[]) //回溯算法求解向量x
{
if (i>=n)
dfs
dfs
dfs
dfs
dfs
dfs
({1,2,3},3,3) ({1,3,2},3,3) ({2,1,3},3,3) ({2,3,1},3,3) ({3,2,1},3,3) ({3,1,2},3,3)
(1,2 3) (1,2 3) (1,2 3) (1,2 3) (1,2 3) (1,2 3)
交换 a[0],a[0]
{ swap(a[i],a[j]);
dfs(a,n,i+1); swap(a[i],a[j]);
交换 a[0],a[0]
dfs ({1,2,3},3,0)
交换 a[0],a[1]
int main() { int a[]={1,2,3},n;
n=sizeof(a)/sizeof(a[0]0; dfs(a,n,0); return 0; }
1+2+3+4+5+6+7+8+9 45
1+2+3+4+5+6+7+8-9
36
1+2+3+4+5+6+7+89
117
1+2+3+4+5+6+7-8+9
({1,2,3},3,2) ({1,3,2},3,2) ({2,1,3},3,2) ({2,3,1},3,2) ({3,2,1},3,2) ({3,1,2},3,2)
交换a[2],a[2]
交换a[2],a[2] 交换a[2],a[2]
交换a[2],a[2] 交换a[2],a[2]
交换a[2],a[2]
{1,2,3}
交换 a[0],a[1]
交换 a[0],a[2]
交换 a[1],a[1]
{1,2,3}
{2,1,3}
{3,2,1}
交换
交换
a[1],a[2] a[1],a[1]
交换
交换
a[1],a[2] a[1],a[1]
交换 a[1],a[2]
{1,2,3} {1,3,2} {2,1,3} {2,3,1} {3,2,1}
算法设计与分析
吴粉侠
第5章 回溯法
回溯法应用
全排列求解
【例5.5】有一个含n个整数的数组a,所有元素均不相同,求其所 有元素的全排列。
例如,n=3,a[]={1,2,3} 全排列: (1,2,3)、(1,3,2)、(2,3,1) (2,1,3)、(3,1,2)、(3,2,1)
初始为1
{1}
将2插入到各位上 {1 2}
01
dfs(3,[0,0,0]) dfs(3,[0,0,1]) dfs(3,[0,1,0]) dfs(3,[0,1,1]) dfs(3,[1,0,0]) dfs(3,[1,0,1]) dfs(3,[1,1,0]) dfs(3,[1,1,1])
{} {3} {2} {2,3} {1} {1,3} {1,2}{1,2,3}
displaySolution(a,n,x);
else
{
x[i]=0; dfs(a,n,i+1,x);
//不选择a[i]
x[i]=1; dfs(a,n,i+1,x);
//选择a[i]
}
}
算法设计
void displaySolution(int a[],int n,int x[]) //输出一个解 {
添加3
{{ 3 },{1,3}, {2,3},{1,2,3}}
1~3的幂集: { { },{1}, { 2 },{1,2} {3},{1,3}, {2,3}, {1,2,3}
}
求解过程分为3步: 决策1:确定1是否要先择 决策2:确定2是否要选择 决策3:确定3是否要选择
解向量X: 决策1:x1 决策2:x2 决策3:x3
//搜索到叶子结点,输出一个可行解
输出结果;
else
{ for (j=i;j<=n;j++)
//用j枚举i所有可能的路径
{…
//第i层的结点选择x[j]的操作
swap(x[i],x[j]);
//为保证排列中每个元素不同,通过交换来实现
if (constraint(i) && bound(i))
backtrack(i+1); //满足约束条件和限界函数,进入下一层
1
F (0,1,*) G
0 1
(0,0,*)
0
元素3的选择
和不选择
L
M
N
O
(1,1,1) (1,1,0) (1,0,1) (1,0,0) (0,1,1) (0,1,0) (0,0,1) (0,0,0)
{1,2,3} {1,2} {1, 3} {1} {2,3}
{2} {3} { }
解: 采用深度优先搜索思路。 每个元素只有两种扩展,要么选择,要么不选择; 解向量为x[],x[i]=0表示不选择a[i],x[i]=1表示选择a[i]。 用i扫描数组a,也就是说问题的初始状态是(i=0,x的元素均为0),目标状
f(a,n,k) for (j=i;j<n;j++)
其他情况
{ 交换a[i]与a[j];
f(a,n,i+1);
交换a[i]与a[j];//恢复环境
}
解空间为排列树时的递归回溯算法框架
int x[n];
//x存放解向量,并初始化
void backtrack(int i)
//求解排列树的递归框架
{ if(i>n)
0
1
dfs(2,[0,0,0])
0
1
dfs(2,[0,1,0])
0
1
dfs(1,[1,0,0])
0
1
dfs(2,[1,0,0])
0
1
dfs(2,(1,1,0])
0
1
dfs(3,[0,0,0]) dfs(3,[0,0,1]) dfs(3,[0,1,0]) dfs(3,[0,1,1]) dfs(3,[1,0,0]) dfs(3,[1,0,1]) dfs(3,[1,1,0]) dfs(3,[1,1,1])
{2 1}
求解的中间结果
将3插入到各位上
{1 2 3} {3 1 2}{2 1 3} {3 2 1}
{1 3 2}
{2 3 1}
求解所需的 最终结果
求解过程
1.确定a[0]
a={1,2,3}
取1
取2
取3
2.确定a[1]
a={1,2,3}
取2
取3
a={2,1,3}
取1
取3
a={3,2,1}
取2
取1
a={1,2,3} a={1,3,2} a={2,1,3} a={2,3,1} a={3,2,1} a={3,1,2}
X=(x1,x2,x3) X=(1,0,1)
{1,3}
求集合{1,2,3}的幂集的解空间树
X=(x1,x2,x3),xi=1,0 (1<=i<=3)
(*,*,*)
1
A
(1,*,*) B
1
0
(1,1,*) D
10
(1,0,*) E
10
H
I
J
K
0
元素1的选择
C (0,*,*)
1
0
ຫໍສະໝຸດ Baidu
和不选择
元素2的选择
和不选择