I.はじめに
01バックパックは、ブログにも説明される前に、ここではそれらを繰り返すない、典型的な動的なプログラミングソリューションです。
https://blog.csdn.net/Jayphone17/article/details/102553763
ここではいくつかの基本的な理論的な知識をバックトラックに関連しています:
第二に、アルゴリズム設計
(1)問題の解空間を定義します
カート質問を買い物する典型的な01ナップザック問題であり、問題の解決策は、最大容量を超えることなく、n個の項目のその値からの項目のいくつかを選択することです。どの2つの項目と2つだけの状態では、どちらかのショッピングカートに入れていない、またはに入れないでください。0カートに入れていないと、ショッピングカートに1つのプットで表されます。したがって、この問題に対する解決策は、nタプル、および0または1の各成分の値です。
(2)空間的組織ソリューションを決定します
問題の解空間が接触する2 ^ n個の可能な説明をn個の要素の集合のすべての部分集合の数であると言うことができます。
目に見える彼らの入力のために、解空間ツリーの問題、およびバイナリツリーです、深さ解空間のツリーは、n-規模の問題です。図示のように:
(3)検索ソリューションスペースツリー
1.制約:
問題空間カートのソリューションは、2 ^ n個の可能な解決策を含み、一つまたはいくつかの項目の存在がショッピングカートに入れることができない、それは、ショッピングカートに入れる制約が重量台車の上に、物品の総重量か否かを判断する必要がセットです超えた場合は、解決策は実現可能な、そうでない場合は実行可能な解決策ではありません。検索プロセスはもはや実現不可能なノードとその子ノードを主導した検索ではありません。
制約は以下のとおりです。
2.限界条件:
実現可能な解決策は、01ナップザック問題は、複数の目標は、最適なソリューションである、ショッピングカートへのアイテムの概要を、見つけるための最大の問題実行可能な解決策であるかもしれカート。したがって、最適解を見つけるために、スピードを加速するために、収束のための条件を設定する必要があります。
解空間の組織ツリーは、任意の中間ノードZ(中間状態)のために、分岐Zの状態にルートノードからその子孫へZから、決定された(既にショッピングカートに入れられる)を表しますブランチノードの状態は不明です。
すなわち、zはツリー階層がTである解空間にある場合、物品のT-1番目の状態に説明最初の項目が既に決定されています。我々は唯一のz分岐延長に沿った商品のトン容易な種類の状態を判断する必要があります。この時点では、商品の種類の状態が1〜tが決定されています。
しかしながら、商品のT + 1〜N種類の状態が決定されません。
商品の状態Tの種類、我々はそれを設定する前には、商品の合計値、表現CPのカートに入れてきました。
私たちは、RPによって表される項目の合計値のT + 1〜n個の種を設定しました。
だから我々は、CP + RPは、ルートノードZから中間ノードを介してすべての実行可能なソリューションを開始の値にバインドされています。
- 現在の境界値未満又は最適に等しい(bestp初期値で表されるが0である)、その後、より良い解決策は、子孫ノードZへの中間ノードから得ることができない値が検索を継続探索された場合、検索は不要です(プルーン)は、逆に、ノードは、子供や孫のために検索し続けています。
- バウンディング条件:CP + RP <= bestp
3.検索方法:
検索するルート、深さ優先探索(DFS)のアプローチから開始します。これはまた、現在の拡張ノードで、rootになるスリップノット。
私たちは、それがショッピングカートに入れを表す、左分岐拡張ノードに沿って延び、左側のサブ木の枝の値が1である設定することに合意しました。
この時点で、必要に制約が確立するかどうかを判断します
- trueの場合、等の優れた深さ検索を続行し、現在のノードが左の部分木を生成延び、そして。
- あなたが設定しない場合は、切断された拡張ノードは、ショッピングカートに入れていないに代わって、拡張するために右枝の枝を残しました。満足している場合は、このケースでは、我々は余裕条件を決定するために、この時間を持っているので、それは、右のサブツリーの最適なソリューションに沿って生成することができる、拡大し続け、その後、右のノードはスリップノットポイントとなり、最適解を生成することが可能です。
- 最寄りのポイントバックに、親ノードの右ブランチに拡張マージン条件をカットしていない場合。
- 結び目ポイントまで。
第三に、詳細なプロセス
ショッピングカート10の容量がある物品1の4セットがあると仮定し、各項目の重みである:W(2,5,4,2)、値がV(6,3,5,4)です。
(1)初期化。
セットsumwとsumvは総重量だけでなく、すべての項目の合計値をカウントするために使用されました。
sumw = 13、sumv = 18、sumw> 10、全てバーレーン、解決を検索する必要性を説明しません。
ショッピングカート総重量CW = 0を初期化します。カート項目CP = 0への電流の合計値。最適値bestp = 0。
(2)は、ルートノードを拡張IIを生成するノード(第1層)
(3)このとき、重みCW <10、IIノード(第2層)を拡大し続けて
Wこの場合CW +(4)[3] = 11> W = 10、第三の密輸項目が、それはCW + CP bestp、このときRP = 4、CP + RP = 13、bestp = 0よりも大きいか否かを判断することができない説明しました従ってマージン条件、拡張右サブツリーを満たす、ノードは、第4号(第3層)を生成します
(5)扩展4号结点,首先判断cw+w[4]=9<10=W,满足约束条件,扩展左分支,生成5号结点(第四层)
(6)此时5号结点的深度是5>n=物品的个数,此时找到一个最优解,更新最优解的值bestp=13。此时5号结点已经成为死结点。搜索结束,开始回溯。
(7)回溯到4号结点,此时没有剩余物品,rp=0,cp=9,cp+rp=9<13,因此不扩展右边结点,剪枝,所以4号结点成为死结点。
(8)回溯到3号结点,3号结点的左分支已经剪枝,所以3结点也成为死结点。
(9)回溯到2号结点,此时剩余物品有两个,rp=9,cp=6,cp+rp=15>13=bestp,故扩展右分支,生成6号结点。
(10)判断6号结点,cw+w[3]=6<W,满足约束条件,扩展左分支,生成7号结点。
(11)判断7号结点,cw+w[4]=8<W,满足约束条件,扩展左分支,生成8号结点。
此时t=5>n,搜索结束,找到一个最优解,更新bestp=15,8号结点成为死结点,开始回溯。
(12)回溯到7号结点,此时没有剩余物品,rp=0,cp=11,cp+rp=11<bestp=15,不满足限界条件,不扩展7号结点的右子树。7号结点成为死结点。
(13)回溯到6号结点,此时剩余一个物品,rp=4,cp=10,rp+co=14<bestp=15,不满足限界条件,不扩展6号结点的右子树。6号结点成为死结点。
(14)回溯到2号结点,此时左右孩子都已经考察过,所以成为死结点。
(15)回溯到1号结点,此时剩余3个物品,rp=12,cp=0,rp+cp=12<bestp=15,不满足限界条件,所以不扩展1号结点的右子树,1号结点成为死结点。此时搜索结束。
四、伪代码
(1)计算上界
上界是指计算已经装入物品的价值cp与剩余物品的总价值rp之和。
double Bound(int i) //计算上界 { int rp=0; while(i<=n) //计算剩余物品重量和 { rp+=v[i]; i++; } return cp+rp; }
(2)按照约束条件和限界条件进行搜索
t表示当前扩展结点在t层,cw表示当前已经放进购物车的重量和,cp表示已经放进购物车的价值和。
T> N、最適値を記録し、リーフノードに到達した表す場合、結果が返されます。
それ以外の場合は、制約が満たされているかどうかを判断する、出会いは、サブツリーの検索を残しました。
マージン条件か否かを判断する、検索右のサブツリーを満たします。
void Backtrack(int t) //回溯函数 { if(t>n)//已经到达了叶子结点 { for (int j=1;j<=n;j++) { bestx[j]=x[j]; //记录编号 } bestp=cp;//保留当前最优值 return; } if(cw+w[t]<=W)//如果满足条件,扩展并且搜索左子树 { x[t]=1; cw+=w[t]; cp+=v[t]; Backtrack(t+1);//回溯 cw-=w[t]; cp-=v[t]; } if(Bound(t+1)>bestp)//如果满足限界条件,搜索右子树 { x[t]=0; Backtrack(t+1);//回溯 } }
第五に、ソースコード
#include <iostream> #include <algorithm> #include <cstring> using namespace std; #define M 105 int n;//表示物品个数 int W;//表示购物车总载重量 double w[M];//表示对应物品重量 double v[M];//表示对应物品价值 bool x[M];//用来判断是否是否放入购物车 double cw;//当前放进购物车总重量 double cp;//当前放进购物车总价值 double bestp; //当前最优值 bool bestx[M]; //当前最优解 double Bound(int i) //计算上界 { int rp=0; while(i<=n) //计算剩余物品重量和 { rp+=v[i]; i++; } return cp+rp; } void Backtrack(int t) //回溯函数 { if(t>n)//已经到达了叶子结点 { for (int j=1;j<=n;j++) { bestx[j]=x[j]; //记录编号 } bestp=cp;//保留当前最优值 return; } if(cw+w[t]<=W)//如果满足条件,扩展并且搜索左子树 { x[t]=1; cw+=w[t]; cp+=v[t]; Backtrack(t+1);//回溯 cw-=w[t]; cp-=v[t]; } if(Bound(t+1)>bestp)//如果满足限界条件,搜索右子树 { x[t]=0; Backtrack(t+1);//回溯 } } void Knapsack(double W,int n) { //初始化 cw=0; cp=0; bestp=0; double sumw=0; double sumv=0; for (int i=1;i<=n;i++) { sumw+=w[i]; sumv+=v[i]; } if(sumw<=W) { bestp=sumv; cout << "放进购物车的物品最大价值是:" << bestp << endl; return; } Backtrack(1);//回溯 cout << "放入购物车的物品最大价值是:" << bestp << endl; cout << "放进购物车的物品的序号是:"; for (int i=1;i<=n;i++) { if(bestx[i]==1) { cout << i << " " ; } } cout << endl; } int main() { cout << "请输入物品的个数:" << endl; cin >>n; cout << "请输入购物车的重量W:" << endl; cin>>W; cout << "请依次输入没个物品的重量w和价值v,用空格分开"; for (int i=1;i<=n;i++) { cin >> w[i] >> v[i]; } Knapsack(W,n); return 0; }
第六に、業績