一.分支限界法概述
(1)分支限界法就是采用廣度優先的策略,依次搜索活結點所有的分枝,也就額是所有的相鄰結點。在求最優解時采用一個限界函數,計算限界函數值,選擇一個最有利的子節點作為擴展結點,使搜索樹朝着解空間樹上有最優解的分支推進,以便盡快找出一個最優解。
(2)常見的兩種分支限界法
先進先出(FIFO)隊列式:在先進先出的分支限界法中,用隊列作為組織活結點表的數據結構,並按照隊列先進先出的原則選擇結點作為擴展結點。
優先隊列(PQ):用優先隊列作為組織活結點表的數據結構。
二.0-1背包問題
問題:給定n種物品和一背包。物品i的重量是wi,其價值為pi,背包的容量為C。問應如何選擇裝入背包的物品,使得裝入背包中物品的總價值最大?
#include<iostream> #include<queue> using namespace std; const int maxn=99; int n,c; int w[maxn]; int v[maxn]; int bestv=0; int bestx[maxn]; int total=1; //解空間中的節點數累計,全局變量 struct nodetype //隊列中的結點類型 { int no; //結點編號,從1開始 int i; //當前結點在搜索空間中的層次 int w; //當前結點的總重量 int v; //當前結點的總價值 int x[maxn]; //當前結點包含的解向量 double ub; //上界 }; void input() { cout<<"請輸入物品的個數:"<<endl; cin>>n; cout<<"請輸入每個物品的重量及價值(如5 4):"<<endl; for(int i = 1; i <= n; i++) { cin>>w[i]>>v[i]; } cout<<"請輸入背包的容量:"<<endl; cin>>c; } void bound(nodetype &e) //計算分支結點e的上界 { int i=e.i+1; //考慮結點e的余下物品 int sumw=e.w; double sumv=e.v; while((sumw+w[i]<=c)&&i<=n) { sumw+=w[i]; sumv+=v[i]; i++; } if(i<=n) //余下物品只能部分裝入 e.ub=sumv+(c-sumw)*v[i]/w[i]; else e.ub=sumv; } void enqueue(nodetype e,queue<nodetype> &qu) //結點e進隊qu { if(e.i==n) //到達葉子節點,不在擴展對應一個解 { if(e.v>bestv) //找到更大價值的解 { bestv=e.v; for(int j=1;j<=n;j++) bestx[j]=e.x[j]; } } else qu.push(e); //非葉子結點進隊 } void bfs() { int j; nodetype e,e1,e2; queue<nodetype> qu; e.i=0; e.w=0; e.v=0; e.no=total++; for(j=1;j<=n;j++) e.x[j]=0; bound(e); qu.push(e); while(!qu.empty()) { e=qu.front();qu.pop(); //出隊結點e if(e.w+w[e.i+1]<=c) //剪枝,檢查左孩子結點 { e1.no=total++; //建立左孩子結點 e1.i=e.i+1; e1.w=e.w+w[e1.i]; e1.v=e.v+v[e1.i]; for(j=1;j<=n;j++) e1.x[j]=e.x[j]; e1.x[e1.i]=1; bound(e1); //求左孩子的上界 enqueue(e1,qu); //左孩子結點進隊 } e2.no=total++; e2.i=e.i+1; e2.w=e.w; e2.v=e.v; for(j=1;j<=n;j++) e2.x[j]=e.x[j]; e2.x[e2.i]=0; bound(e2); if(e2.ub>bestv) //若右孩子結點可行,則進隊,否則被剪枝 enqueue(e2,qu); } } void output() { cout<<"最優值是:"<<bestv<<endl; cout<<"("; for(int i=1;i<=n;i++) cout<<bestx[i]<<" "; cout<<")"; } int main() { input(); bfs(); output(); return 0; }