学习回溯法与剪枝
回溯法
1.求子集问题
有一个含n个整数的数组a,所有元素均不相同,设计一个算法求其所有子集(幂集)。
思路:解空间为子集树。
使用x[]表示解向量:
- 不选择a[i]元素->下一个状态为(x[i]=0, i+1)
- 选择a[i]元素->下一个状态为(x[i]=1, i+1)
深搜+回溯
1 |
|
2. 符号插入凑整
设计一个算法在1,2,…,9(顺序不能变)数字之间插入+或-或什么都
不插入,使得计算结果总是100的程序,并输出所有的可能性。
例如:1+2+34-5+67-8+9=100.
思路:
利用回溯法
1 |
|
3.全排列问题
有一个含n个整数的数组a,所有元素均不相同,求其所有元素的全排列。例如,a[]={1,2,3},得到结果是(1,2,3)、(1,3,2)、(2,3,1)、(2,3,1)
思路:
1 |
|
回溯法与深搜的异同
相同点
- 都遵循深度优先,即一步一步向前探索
不同点
- 访问序不同:深度优先遍历目的是遍历,本质无序;回溯法目的是求解过程,本质有序
- 访问次数不同:回溯法访问过的节点可能再次访问
- 剪值的不同:回溯算法可采用剪枝条件剪除不必要的分枝以提高效能
0/1背包问题
有n个重量分别为{w1,w2,...wn}
的物品,它们的价值分别为{v1,v2,…vn},给定一个容量为W的背包。设计从这些物品中选取一部分物品放入该背包的方案,每个物品要么选中要么不选中,要求选中的物品不仅能够放在背包中,而且满足重量限制具有最大的价值。
思路:
考虑装入背包中物品重量和恰好为W:
1 |
|
改进1
左剪枝:
仅仅扩展满足tw+w[i]<=W
的节点
改进2
右剪枝:
rw=w[i]+w[i+1]+...+w[n]
仅仅扩展满足tw+rw-w[i]>=W
的节点
1 | void dfs(int i,int tw,int tv,int rw,int op[]){ |
考虑装入背包中物品重量和不超过W:
- 左剪枝相同
- 右剪枝修改:
上界函数bound(i)=tv+r
表示沿着该方向选择得到物品的价值上界,r表示剩余物品的总价值。
若当前bound(i)<=maxv
,则右剪枝,否则继续扩展。
显然r越小,bound(i)也越小,剪枝越多,为了构造更小的r,将所有物品以单位重量价值递减排列。
1 | int bound(int i,int tw,int tv)//求上界 |
右剪枝:仅仅扩展bound(i,tw,tv)>maxv
的右孩子节点
1 | void dfs(int i,int tw,int tv,int rw,int op[]){ |