演算法第五章《回溯法》上機實踐報告
一.實踐題目名稱
最小重量機器設計問題
二.問題描述
設某一機器由n個部件組成,每一種部件都可以從m個不同的供應商處購得。設wij是從供應商j 處購得的部件i的重量,cij是相應的價格。試設計一個演算法,給出總價格不超過d的最小重量機器設計。
輸入格式:
第一行有3 個正整數n ,m和d, 0<n<30, 0<m<30, 接下來的2n 行,每行n個數。前n行是c,後n行是w。
輸出格式:
輸出計算出的最小重量,以及每個部件的供應商
三.演算法描述
1.請用回溯法的方法分析“最小重量機器設計問題”
遍歷順序:從一個節點開始,以深度優先的方法搜尋解空間,每一個葉子結點都代表著一個可能性,輸出
剪枝方式:
1)約束法:以:“當前價格+該零件價格<總價格d”作為判斷條件,刪除不滿足約束的子樹。
2)限界法:以:“當前重量+該零件重量<最小重量紀錄”作為判斷條件,將一定不為最優解的子樹刪除。
1.1說明“最小重量機器設計問題"的解空間
本題的解空間為,長度為n的供應商各種組合向量中,總價格不超過d的每個部件的供應商選擇的集合所有可能的部件重量值,{(1,3,1),(1,3,2),(1,3,3)}。
1.2說明“最小重量機器設計問題"的解空間樹
解空間為一棵m叉樹,共有n層,每一層分別表示需要一個零件,每一個節點的m個分支表示對於每個零件都有
1.3在遍歷解空間樹的過程中,每個結點的狀態值是什麼
間為一該路徑上從第一個節點到該節點的當前總價值cv,和當前總重量cw。
#include<iostream> using namespace std; int n,m,d; int c[999][999],w[999][999]; int cw=0,cp=0;//記錄該分支最小的重量和價值 int minw=999; int x[999];//儲存當前重量下每個部件供應商的號碼 int minx[999]; void backtrack(int i) { //遍歷到了子葉 if(i>n) {if(cp<=d&&cw<minw) { minw=cw; for(int j=1; j<=n; j++) { minx[j]=x[j]; } } } else { for(int j=1; j<=m; j++) { x[i]=j;//記錄下當前部件是哪一個供應商 cw=cw+w[i][j]; cp=cp+c[i][j]; if(cp<=d&&cw<minw) { backtrack(i+1); } //回溯 cw=cw-w[i][j]; cp=cp-c[i][j]; } } } int main() { //n個元件 m個供應商 總價格不超過d cin>>n>>m>>d; //第i個零件在第j個產家的價錢 for(int i=1; i<=n; i++) for(int j=1; j<=m; j++) cin>>c[i][j]; //第i個零件在第j個產家的重量 for(int i=1; i<=n; i++) for(int j=1; j<=m; j++) cin>>w[i][j]; //從第一個零件開始遍歷 backtrack(1); //輸出 cout<<minw<<endl; for(int i=1; i<=n; i++) cout<<minx[i]<<" "; return 0; }
四.對回溯法的理解
回溯法是一個採用深度優先的策略,從根節點開始進行深度遍歷,先判斷該節點是否包含問題的解。如果不包含,則跳過對以該節點為根的子樹的搜尋,回溯到其它祖先節點,直到所有子樹都被遍歷完成,但如果不進行有效的剪枝的話,解空間是非常龐大的,所以我們在平時一定要用約束法和限界法進行有效的剪枝,以節省時間和空間上的開銷。