文章目录
- 上一篇
- 回溯法性质
- 子集和问题
- 装载问题
- 下一篇
上一篇
算法设计与分析复习–贪心(二)
回溯法性质
类似穷举的搜索尝试过程,在搜索尝试过程中寻找问题的解,组织得井井有条(避免遗漏), 高效(剪裁避免不必要搜索)
使用深度优先的搜索策略(DFS + 剪枝)
回溯法的阶梯框架:
- 子集树算法框架
- 排序树算法框架
结点类型:
- 活结点:自身已生成但是其儿子还没有全部生成
- 拓展结点:正在产生儿子的结点
- 死结点:不满足约束或所有儿子已经产生,不能向纵深方向移动
为了避免生成那些不可能产生最优解的问题状态,要不断利用限界函数,处死结点=>剪枝
剪枝策略:
- 可行性剪枝,左剪枝
- 最优性剪枝,右剪枝
- 交换搜索顺序,排序方式变化
解空间类型:
-
子集树:所给的问题是从n个元素集合S中找出满足某种性质的子集。
-
排列数:所给问题是确定n个元素满足某种性质的排列。
采用交换方式实现的全排列(顺序有点问题)
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 10;
int a[N], n;
int path[N];
void dfs(int* ans, int k)
{
if (k == n - 1){
for (int i = 0; i < n; i ++)
printf("%d ", ans[i]);
puts("");
}
for (int i = k; i < n; i ++){
swap(ans[k], ans[i]);
dfs(ans, k + 1);
swap(ans[k], ans[i]);
}
}
int main()
{
scanf("%d", &n);
for (int i = 0; i < n; i ++) path[i] = i + 1;
dfs(path, 0);
return 0;
}
基于子集树框架的问题求解:
- 子集和问题
- 装载问题
- 0-1背包问题
- 图的m着色问题
基于排列树框架的问题求解:
- 旅行商问题(TSP)
- N皇后问题
子集和问题
问题描述:给定n个不同正实数的集合W = {w(i) | 1 <= i <= n}和一个正整数M, 要求找到子集S使得求和为M。
子集和问题要求出所有可行解
左剪枝:求和不超过M => cw + a[i] <= M
右剪枝:当前已选的价值与剩余的数的价值的和要大于M否则不可能找到 => cw + rw >= M
#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
const int N = 110;
int a[N], n, M, cw, rw;
vector<int> ans;
void dfs(int k)
{
if (k == n)//根节点是0叶结点就是n
{
if (cw == M){
for(auto i : ans)
printf("%d ", i);
puts("");
}
return;
}
rw -= a[k];
if (cw + a[k] <= M){//左剪枝条件
cw += a[k];
ans.push_back(a[k]);
dfs(k + 1);//向左走
ans.pop_back();
cw -= a[k];
}
if(rw + cw >= M)//右剪枝条件
dfs(k + 1);//向右走
rw += a[k];
}
int main()
{
scanf("%d%d", &n, &M);
for (int i = 0; i < n; i ++) scanf("%d", &a[i]), rw += a[i];
dfs(0);
return 0;
}
装载问题
和贪心中的装载问题不同
问题描述:n个集装箱要装到2艘载重量分别c1,c2的货轮,其中集装箱
i
i
i 的重量 为
w
i
w_i
wi。要求找到合理的装载方案将这n个货箱装上这2艘轮船。
要使两个船都装上,可以考虑将第一个船尽可能装满,然后将剩下的货物装在第二艘船上,如果没超重就是可行的,将考虑两个船的问题转换成考虑一个。
为找到将左边轮船撞得最满的方案,用bestw进行限界
#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
const int N = 110;
int a[N], n, c1, c2;
int cw, bw, rw, other;
vector<int> ans;
void dfs(int k)
{
if (k == n){
bw = cw;
if(other > c2) return;//other是另一艘船货物重量
for (auto i : ans)
printf("%d ", i);
puts("");
return;
}
rw -= a[k];
if (cw + a[k] <= c1)
{
cw += a[k];
ans.push_back(a[k]);
dfs(k + 1);
ans.pop_back();
cw -= a[k];
}
if (cw + rw >= bw){
other += a[k];
dfs(k + 1);
other -= a[k];
}
rw += a[k];
}
int main()
{
scanf("%d%d%d", &n, &c1, &c2);
for (int i = 0; i < n; i ++) scanf("%d", &a[i]), rw += a[i];
dfs(0);
return 0;
}
下一篇
算法设计与分析复习–回溯法(二)