揹包問題的遞迴和非遞迴演算法
阿新 • • 發佈:2019-01-28
/** 簡單揹包問題 問題定義: 有一個揹包重量是S,有n件物品,重量分別是W0,W1...Wn-1 問能否從這n件物品中選擇若干件放入揹包中使其重量之和正好為S */ #include <iostream> #include <algorithm> #include <vector> #include <string> using namespace std; const int maxsize=100; int n,S;//n是有多少中物品,S是要湊足的重量 bool visit[maxsize];//標記是否被訪問過,別訪問過標記為1,沒有訪問過為0 int w[maxsize];//記錄每一種物品的重量 int q[maxsize];//相當於一個棧,儲存被訪問過的物品的編號 int beibao() { int top=-1,begin=0,sum=0; int i; while(top!=-2) { //從第一個物品開始迴圈 for(i=begin;i<n;i++) { //如果沒有被訪問過,並且加上重量之和小於S if(!visit[i] && w[i]+sum<=S) { sum+=w[i];//在sum上加上當前物品的重量 q[++top]=i;//把物品的編號存入q[]中 begin=0;//從頭開始訪問 visit[i]=1;//該結點訪問過標記 if(sum==S) return top;//如果成功,返回top,top為陣列元素的個數,有所有物品的編號 break; } } //如果檢索到最後,也就是說棧頂前面的物品都不符合條件 //因此可能棧內的元素有問題,所以彈出棧頂元素,不把棧頂元素計算在內 if(i==n) { visit[q[top]]=0;//把棧頂元素定義成未訪問 sum-=w[q[top]];//從和中減去站定物品編號的重量 begin=q[top]+1;//從棧頂元素的下一個物品開始檢索 //cout<<"------"<<begin<<endl; top--;//棧遞減一個單位 } } } //揹包問題遞迴版本 /** 解釋:其選擇只有兩種可能,選擇一組物品中包含Wn-1 ,此時knap(s,n)的解就是knap(s - Wn-1,n-1)的解 如果選擇的物品中不包括Wn-1,這樣knap(s,n)的解就是knap(s,n-1)的解 knap(s,n) = true, 當 s=0時 false ,當s < 0 或者 s > 0 且 n < 1 knap(s - Wn-1,n-1) || knap(s,n-1) 當s>0且n>=1 */ bool knap(int s,int n) { if(s == 0) return true;//遞迴出口 在s恰好為0時 遞迴結束 if(s < 0 || (s > 0&&n < 0)) return false;//或者s小於0 n 小於0時 if(knap(s - w[n - 1],n - 1)) { cout<<w[n - 1]; return true; }else return knap(s,n - 1); } int main() { cin>>n>>S; for(int i=0;i<n;i++) { cin>>w[i]; visit[i]=0; } int t=beibao(); knap(S,n); if(t!=-1) { for(int i=0;i<=t;i++) cout<<w[q[i]]<<" "; cout<<endl; } else cout<<-1<<endl; }