您的位置:首页 > 其它

分支限界法(算法分析与设计)

2015-10-20 15:42 603 查看
0.概念

分支限界法常以广度优先(队列式(先进先出)分支限界)或以最小耗费(最大效益)优先的方式(优先队列分支限界)搜索问题的解空间树。

在分支限界法中,每一个活结点只有一次机会成为扩展结点。活结点一旦成为扩展结点,就一次性产生其所有儿子结点。在这些儿子结点中,导致不可行解或导致非最优解的儿子结点被舍弃,其余儿子结点被加入活结点表中。此后,从活结点表中取下一结点成为当前扩展结点,并重复上述结点扩展过程。这个过程一直持续到找到所需的解或活结点表为空时为止。

1.与回溯法的不同

求解目标:回溯法的求解目标是找出解空间树中满足约束条件的所有解,而分支限界法的求解目标则是找出满足约束条件的一个解,或是在满足约束条件的解中找出在某种意义下的最优解。

搜索方式的不同:回溯法以深度优先的方式搜索解空间树,而分支限界法则以广度优先或以最小耗费优先的方式搜索解空间树。

2.广度优先或以最小耗费优先的方式

广度优先:与层次遍历相似,唯一的不同之处在于不再遍历不可行节点

最小耗费优先与贪心算法的不同

从字面上的意思理解,最小耗费优先,是以最小耗费优先计算,并不是不将较大耗费的节点作为活节点了,只是在作为活节点的时候将较大耗费的那支给剪了;而贪心算法,是只计算最小耗费的节点,较大耗费的节点就不再作为活节点了。

A算法(启发式搜索)、贪心、分支限界求最优解的区别

对当前优先搜索方向的判断标准为< 有可能的最优解 >。而最优解可以用一个评估函数来做,即已经有的耗散度加上以后有可能的耗度。A算法就是把两个耗散度加在一起,作为当前状态的搜索搜索方向;但是对以后的耗散度的评估是麻烦的,D(迪杰斯特拉,贪心)算法就是把当前有的路的最短的作为,以后耗散度的评估。分支限界算法就是只以以前的耗散度为评估函数。

3.单源最短路径

A.优先队列式分支界限



#include<iostream>
#include<vector>
#include<climits>
#include<queue>
using namespace std;
vector< vector<int> > v;
vector<int> dist;
int num;
struct Node
{
int i;//顶点i
int len;//起点到顶点i的距离
};
struct cmp
{
bool operator ()(Node a,Node b)
{
return a.len>b.len;
}
};
void shortest(int start)
{
priority_queue<Node,vector<Node>,cmp> q;
Node startv;
startv.i=start;
startv.len=0;
q.push(startv);
dist[start]=0;
while(true)
{
for(int j=0;j<num;j++)//到哪个节点距离小,就将哪个节点作为扩展节点,并不是要按部就班的深度搜索,也不是以后不将耗费较大的做活节点了
{
if(q.top().len+v[q.top().i][j]<dist[j]&&v[q.top().i][j]!=-1)
{
dist[j]=q.top().len+v[q.top().i][j];
Node temp;
temp.i=j;
temp.len=dist[j];
q.push(temp);
}
}
if(q.empty())
{
break;
}
else
{
q.pop();
}
}
}
int main()
{
cin>>num;
for(int i=0;i<num;i++)
{
vector<int> temp1;
for(int j=0;j<num;j++)
{
int temp2;
cin>>temp2;//-1表示没有边,正数表示存在边
temp1.push_back(temp2);
}
v.push_back(temp1);
dist.push_back(INT_MAX);
}
shortest(0);
cout<<dist[num-1]<<endl;
}

4.装载问题

子集树

A.队列式分支限界

主要就是用队列层次遍历,当然过程中会剪掉一些明显的不合条件的分支

#include<iostream>
#include<vector>
#include<queue>
#include<cstdlib>
using namespace std;
struct Node
{
int i;
int w;//到此为止的总重量
};
vector<int> w;
int c1;
int c2;
int num;
int sumw;
int bestcost;
int random(int s,int e)
{
return s+rand()%(e-s);
}
void loading()
{
if(sumw<c1)
{
bestcost=sumw;
return;
}
queue<Node> q;
Node temp;
temp.i=0;
temp.w=0;
q.push(temp);//无效根节点
while(!q.empty()&&q.front().i<num)
{
Node t=q.front();
q.pop();
for(int i=0;i<=1;i++)
{
int tempw=t.w+i*w[t.i+1];
if(tempw<=c1)
{
temp.i=t.i+1;
temp.w=tempw;
q.push(temp);
if(sumw-tempw<=c2&&tempw>bestcost)
{
bestcost=tempw;
}
}
}
}
}
int main()
{
bestcost=-1;
sumw=0;
cin>>num>>c1>>c2;
for(int i=0;i<=num;i++)//0作为无效根节点
{
int temp=random(1,50);
sumw+=temp;
w.push_back(temp);
cout<<temp<<"  ";
}
cout<<endl;
w[0]=0;

loading();

if(bestcost!=-1)
cout<<"c1: "<<bestcost<<"\nc2: "<<sumw-bestcost<<endl;
else
cout<<"can't loading"<<endl;
return 0;
}

B.优先队列式分支限界

注意此处优先级的设置是当前载重量+剩余未选择货物的总重量

#include<iostream>
#include<vector>
#include<queue>
#include<cstdlib>
using namespace std;
struct Node
{
int i;
int w;//到此为止的总重量
int r;//权重,权重等于w+r[i],剩余货物i+1~num的总重量,因为剩余货物i+1~num有可能全选择,所以考虑权重时要把它也带上
};
struct cmp
{
bool operator()(Node a,Node b)
{
return a.r<b.r;//大值优先
}
};
vector<int> w;
int c1;
int c2;
int num;
int sumw;
int bestcost;
int random(int s,int e)
{
return s+rand()%(e-s);
}
void loading()
{
if(sumw<=c1)
{
bestcost=sumw;
return;
}
vector<int> r;
for(int i=0;i<=num;i++)
{
r.push_back(0);
}
for(int i=num-1;i>0;i--)//剩余货物总重量
{
r[i]=r[i+1]+w[i+1];
}

priority_queue<Node,vector<Node>,cmp> q;
Node temp;
temp.i=0;
temp.w=0;
temp.r=0;
q.push(temp);
int countN=0;
while(!q.empty()&&q.top().i<num)//大顶堆,一直取最大的(不一定是一个分支上),所以只要到达叶子节点就表示已经得最优解了
{
countN++;
Node t=q.top();
q.pop();
for(int i=0;i<=1;i++)
{
temp.w=t.w+i*w[t.i+1];
temp.i=t.i+1;
temp.r=temp.w+r[temp.i];
if(temp.w<=c1)
{
q.push(temp);
}
}
}
cout<<countN<<endl;
bestcost=q.top().w;
}
int main()
{
bestcost=-1;
sumw=0;
cin>>num>>c1>>c2;
for(int i=0;i<=num;i++)//0作为无效根节点
{
int temp=random(1,50);
sumw+=temp;
w.push_back(temp);
cout<<temp<<"  ";
}
cout<<endl;
w[0]=0;
loading();
if(bestcost!=-1)
cout<<"c1: "<<bestcost<<"\nc2: "<<sumw-bestcost<<endl;
else
cout<<"can't loading"<<endl;
return 0;
}
C.比较

队列式分支界限法,是将整个解空间树给搜索了一遍,效率比较低。实验测得:当n=10,c1=200,c2=200,wi随机产生时,队列式分支限界的循环次数为1022次,优先队列分支限界的循环次数是53次。

5.TSP

排列树

A.队列式分支限界

#include<iostream>
#include<queue>
#include<vector>
#include<cstdlib>
#include<climits>
using namespace std;
vector< vector<int> > v;
vector<bool> x;
int num;
int bestcost;
struct Node
{
int i;
int cost;
vector<bool> x;
};
int random(int s,int e)
{
return s+rand()%(e-s);
}
void tsp(int start)
{
queue<Node> q;
Node t;
t.i=start;
t.x=x;
t.cost=0;
t.x[start]=true;
q.push(t);
while(!q.empty())
{
Node te=q.front();
q.pop();
int flag=0;
for(int i=0;i<num;i++)
{
if(!te.x[i])
{
t.i=i;
t.x=te.x;
t.cost=te.cost+v[te.i][t.i];
t.x[i]=true;
q.push(t);
flag=1;
}
}
if(!flag)//叶子节点了
{
if(te.cost+v[te.i][start]<bestcost)
{
bestcost=te.cost+v[te.i][start];
}
}
}
}
int main()
{
bestcost=INT_MAX;
cin>>num;
for(int i=0;i<num;i++)
{
vector<int> temp;
for(int j=0;j<num;j++)
{
temp.push_back(0);
}
v.push_back(temp);
x.push_back(false);
}
for(int i=0;i<num;i++)
{
for(int j=i+1;j<num;j++)
{
int temp=random(1,50);
v[i][j]=temp;
v[j][i]=temp;
}
}
tsp(0);
cout<<bestcost<<endl;
}
B.优先队列分支限界
优先级是当前路径总量+未排序节点最小出路总量

#include"tsp.h"
using namespace std;
struct Node
{
int i;//第i层
int ncost;//当前花费
int rcost;//剩余节点的最小出边和
int lcost;//优先级,为当前花费和剩余节点的最小出边和之和
vector<int> x;
};
struct cmp
{
bool operator()(Node a,Node b)
{
return a.lcost>b.lcost;
}
};
void branchboundPQ(const vector< vector<int> > &v,vector<int> &x,int &costbest,int firstcity)
{

swap(x[firstcity],x[0]);
int minsum=0;//最小出边和
vector<int> minout;//每个顶点的最小出边
int num=x.size();
for(int i=0;i<num;i++)
{
int min=INT_MAX;
for(int j=0;j<num;j++)
{
if(v[x[i]][x[j]]>0&&v[x[i]][x[j]]<min)
{
min=v[x[i]][x[j]];
}
}
minout.push_back(min);
minsum+=min;
}
priority_queue<Node,vector<Node>,cmp> p;
Node t;
t.i=0;
t.ncost=0;
t.rcost=minsum;
t.lcost=0;
t.x=x;
p.push(t);
while(!p.empty()&&p.top().i<num-1)
{
Node te=p.top();
p.pop();
if(te.i==num-2)
{
int costT=te.ncost+v[te.x[num-2]][te.x[num-1]]+v[te.x[num-1]][te.x[0]];
if(costT<costbest)
{
costbest=costT;
te.ncost=costbest;
te.lcost=costbest;
te.i++;
p.push(te);
}
}
else
{
for(int i=te.i+1;i<num;i++)
{
t.i=te.i+1;
t.ncost=te.ncost+v[te.x[te.i]][te.x[i]];
t.rcost=te.rcost-minout[x[te.i]];
t.lcost=t.ncost+t.rcost;
t.x=te.x;
t.x[te.i+1]=te.x[i];
t.x[i]=te.x[te.i+1];
if(t.lcost<costbest)//当前花费和剩余节点的最小出边和之和小于最小花费,说明可能存在更优解
{
p.push(t);
}
}
}
}
}

6.0-1背包

子集树

A.队列式分支限界

#include<iostream>
#include<vector>
#include<cstdlib>
#include<queue>
using namespace std;
struct Node
{
int i;
int w;//到i为止的总重量
int v;//到i为止的总价值
};
int num,c,maxv;
vector<int> w;
vector<int> v;
int random(int s,int e)
{
return s+rand()%(e-s);
}
void knapsack()
{
Node t;
t.i=-1;
t.w=0;
t.v=0;
queue<Node> q;
q.push(t);
while(!q.empty())
{
Node te=q.front();
q.pop();
for(int i=0;i<=1;i++)
{
int tempw=te.w+i*w[te.i+1];
int tempv=te.v+i*v[te.i+1];
if(tempw<=c)
{
t.i=te.i+1;
t.w=tempw;
t.v=tempv;
if(t.i==num-1&&t.v>maxv)
{
maxv=tempv;
}
else if(t.i<num-1)
{
q.push(t);
}
}
}
}
}
int main()
{
cin>>num>>c;
maxv=-1;
for(int i=0;i<num;i++)
{
int temp=random(1,20);
w.push_back(temp);
cout<<temp<<"(";
temp=random(1,50);
v.push_back(temp);
cout<<temp<<") ";
}
cout<<endl;
knapsack();
cout<<maxv<<endl;
return 0;
}
B.优先队列式分支限界
优先级:当前总价值+剩余未选择物品总价值

#include<iostream>
#include<vector>
#include<queue>
#include<cstdlib>
using namespace std;
struct Node
{
int w;//当前重量
int v;//当前总价值
int i;//层号
int p;//优先级:当前价值+剩余未抉择物品价值
};
struct cmp
{
bool operator()(Node a,Node b)
{
return a.p<b.p;
}
};
vector<int> w;
vector<int> v;
int num,c,maxv,sumv;

int random(int s,int e)
{
return s+rand()%(e-s);
}
void knapsack()
{
vector<int> r;
priority_queue<Node,vector<Node>,cmp> q;
for(int i=0;i<num;i++)
{
sumv=sumv-v[i];
r.push_back(sumv);
}
Node t;
t.i=-1;
t.w=0;
t.v=0;
t.p=0;
q.push(t);

while(!q.empty()&&q.top().i<num-1)
{
Node te=q.top();
q.pop();
for(int i=0;i<=1;i++)
{
int tempw=te.w+i*w[te.i+1];
int tempv=te.v+i*v[te.i+1];
int tempp=tempv+r[te.i+1];
if(tempw<=c)
{
t.w=tempw;
t.v=tempv;
t.p=tempp;
t.i=te.i+1;
if(t.p>maxv)
{
q.push(t);
}
}
}
}
maxv=q.top().v;
}
int main()
{
maxv=-1;
sumv=0;
cin>>num>>c;
for(int i=0;i<num;i++)
{
int temp=random(1,20);
w.push_back(temp);
cout<<temp<<"<";
temp=random(1,50);
v.push_back(temp);
cout<<temp<<"> ";
sumv+=temp;

}
cout<<endl;
knapsack();
cout<<maxv<<endl;
}

7.最大团问题

A.优先级队列式分支限界

#include<iostream>
#include<vector>
#include<queue>
using namespace std;
struct Node
{
int i;
int cnum;//该节点所在团,当前拥有的个数
vector<int> x; //该节点所在团成员
};
struct cmp
{
bool operator()(Node a,Node b)
{
return a.cnum<b.cnum;
}
};
vector< vector <int> > v;
int num;

bool judge(const vector<int> &d,int xi)
{
for(int i=0;i<xi;i++)
{
if(d[i]==1&&v[i][xi]==0)
{
return false;
}
}
return true;
}
int maxclique(vector<int> &x)
{
priority_queue<Node,vector<Node>,cmp> q;
Node t;
t.i=-1;
t.cnum=0;
t.x=x;
q.push(t);
while(!q.empty()&&q.top().i<num-1)
{
Node te=q.top();
q.pop();
//0
t.i=te.i+1;
t.cnum=te.cnum;
t.x=te.x;
q.push(t);
//1
if(judge(te.x,te.i+1))
{
t.i=te.i+1;
t.cnum=te.cnum+1;
vector<int> tempx=te.x;
tempx[t.i]=1;
t.x=tempx;
q.push(t);
}
}
for(int i=0;i<num;i++)
{
if(q.top().x[i]==1)
{
cout<<i+1<<" ";
}
}
cout<<endl;
return q.top().cnum;
}
int main()
{
vector<int> x;
cin>>num;
for(int i=0;i<num;i++)
{
vector<int> temp;
for(int j=0;j<num;j++)
{
temp.push_back(0);
}
v.push_back(temp);
x.push_back(0);
}
for(int i=0;i<num;i++)
{
for(int j=i+1;j<num;j++)
{
int temp;
cin>>temp;
v[i][j]=temp;
}
}
cout<<maxclique(x)<<endl;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: