分支限界法C++ 学习&练习
Posted 图灵奖未来得主
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了分支限界法C++ 学习&练习相关的知识,希望对你有一定的参考价值。
分支限界法C++ 学习&练习
分支界限法
许多问题的解决实际上是多步决策问题,每走一步有都有多步动作可以选择,逐步形成一个状态空间搜索树。可以通过约束函数和限界函数可以把分支去掉。
剪枝:用限界函数/约束函数避免无效搜索。
约束函数:不能满足约束的子树不再搜索
限界函数:不能得到最优价值的子树不再搜索
(当我们知道从当前的状态探索下去,不可能优于当前已知的最优价值,就没有必要探索下去了)
已背包问题为例:最大值
·当前已有可行解的最大价值,记为B。
·当前(状态)结点N子树的最大价值bound(N)。
if bound(N)<B 不需要再探索结点N了
背包问题中:
bound(N)=当前已经装入价值+剩下的能装最大价值
即 cv+remaining_value<bestV(已知的最优值)
深度优先搜索-分支限界
//模板一:
dfs(c) ://c是当前状态
if reject(c) : return;//reject以上两种情况
if accept(c) :output, return;
for (c的每个可达状态s)
dfs(s);
//模板二:
dfs(c) ://c是当前状态
if accept(c) : output();
for c的每个可达状态s:
push(s);
if not reject(s):
dfs(s)
pop()//用来保存路径
广度优先搜索-分支限界
bfs():
初始化B为无穷大(或无穷小)
启发式搜索(如贪婪)得到可行解更新B
初始化状态队列
while 队列不空
出队一个结点N
if N 表示一个可行解x //如达到叶子结点
if f(x)优于B:则B=f(x)
else
for 每个分支N
if (bound(N)优于B)
N入队
练习
0/1背包问题
分析:按照单位重量价值排序,分别判断“好”的物品要不要选
bound©=cv+(W-cw)*(Vi+1)/(Wi+1)
当前结点为xi,那么下面可以选择的剩下的最好的单位重量(Vi+1)/(Wi+1)装满(但实际上不一定正好,只能说这样求出来的是最好的),如果最好的都比当前的最小重量小,那直接别看了(这是当前结点最大价值的上界)
具体来看每一步:
给定:w=(4,7,5,3),v=(40,42,25,12),W=10
1.cw=0,cv=0,ub=100
2.x1=1,cw=4,cv=40,ub=76 B=40
3.x1=1 x2=1 不满足约束条件
4.x1=1 x2=0 cw=4 cv=40 ub=70 B=40
5.x1=1 x2=0 x3=1 cw=9 cv=65 ub=69 B=65
6.x1=1 x2=0 x3=1 x4=1 不满足约束条件
然后再回溯找有没有更小的啦~
最后找到最好的是65
伪代码:
对items按v/w排序
B=0
初始化状态队列,初始状态进队列
while队列不空
出队一个结点N
if N表示可行解:continue;
for 每个分支Ni
if Ni是可行解且cv(Ni)优于B:则B=cv(Ni);//f(x)
if bound(Ni)优于B:
Ni入队
例题:1. 利用分支限界法求 0-1 背包问题。有 n = 20 个物品,背包最大可装载 M = 878 Kg。物品重量和价值分别如下:
W={92,4,43,83,84,68,92,82,6,44,32,18,56,83,25,96,70,48,14,58},
V={44,46,90,72,91,40,75,35,8,54,78,40,77,15,61,17,75,29,75,63},
求最优背包价值。
这里适用深度优先遍历写的代码
#include<iostream>
#include<algorithm>
using namespace std;
static int B = 0;//初始化最优值
static int M = 878, n = 20;
typedef struct item {
int w, v;
double good;
};
bool cmp(item a, item b) {
return a.good >= b.good;
}
void create(item items[], int x[], int y[]) {
for (int i = 0; i < 20; i++) {
items[i].w = x[i];
items[i].v = y[i];
items[i].good = (y[i] * 1.0) / x[i];
}
sort(items, items + 20, cmp);
}
void dfs(item items[],int i,int cv,int cw) {
if (i >= 20) return;
cv += items[i].v;
cw += items[i].w;
if (cw <= M && (cv + items[i + 1].good * (M - cw)) >= B) {
if (cv > B) {
B = cv;
}
dfs(items, i + 1,cv,cw);
cv -= items[i].v;
cw -= items[i].w;
dfs(items, i + 1, cv, cw);
}
else {
cv -= items[i].v;
cw -= items[i].w;
dfs(items, i + 1, cv, cw);
}
}
int main() {
item items[20];
int x[20] = { 92,4,43,83,84,68,92,82,6,44,32,18,56,83,25,96,70,48,14,58 };
int y[20] = { 44,46,90,72,91,40,75,35,8,54,78,40,77,15,61,17,75,29,75,63 };
create(items, x, y);//创建排序好的items
int cv = 0, cw = 0;
dfs(items,0, cv, cw);
cout << B;
}
以上是关于分支限界法C++ 学习&练习的主要内容,如果未能解决你的问题,请参考以下文章