揹包問題的求解

2021-08-21 03:43:02 字數 4072 閱讀 2764

1、問題描述

假設有乙個能裝入總體積為t的揹包和n件體積分別為w1,w2,…wn的物品,能否從n件物品中挑選若干件恰好裝滿揹包,即使w1+w2+…+wm=t,要求找出所有滿足上述條件的解。

2、設計功能要求

例如:當t=10,各件物品的體積時,可找到下列4組解:

(1,4,3,2)

(1,4,5)

(8,2)

(3,5,2)

3、實現提示

可利用回溯法的設計思想來解決揹包問題。首先,將物品排成一列,然後,順序選取物品裝入揹包,若已選取第i件物品後未滿,則繼續選取第i+1件,若該件物品「太大」不能裝入,則棄之,繼續選取下一件,直至揹包裝滿為止。

如果在剩餘的物品中找不到合適的物品以填滿揹包,則說明「剛剛」裝入的物品「不合適」,應將它取出「棄之一邊」,繼續再從「它之後」的物品中選取,如此重複,直到求得滿足條件的解,或者無解。由於回溯求解的規則是「後進先出」,自然要用到「棧」。

進一步考慮:如果每件物品都有體積和價值,揹包又有大小限制,求解揹包中存放物品總價值最大的問題解---最優解或近似最優解。

如何選取物品,且當已經找到一組解的時候去繼續尋找下一組解。

首先,根據提示,我們需要先定義棧和棧的基本操作:初始化 initstack(seqstack &s),入棧push(seqstack&s,int x),出棧pop(seqstack &s)。然後我們要選取物品,物品可以用陣列定義w[num],這樣的話可以得到物品的編號i,物品的重量w[i]。當體積t大於選中的物品時,將該物品入棧,然後用總體積t減去該物體重量,若剩餘體積大於0,則進一步將物體入棧,若等於0,則可以輸出一組解,否則,將棧頂元素出棧。迴圈進行上述過程,知道棧空且最後一次出棧物品的編號是num。

主要說一下knap函式的功能實現:

定義k作為標識記錄當前入棧物品的編號

s1:當第i物品體積小於t時,將該物品入棧,同時減去該物品體積(t-w[i]),k++;

s2:若體積t大於0或k小於總物品個數,迴圈進行s2;

s3:當t為0時,可輸入一組解;

s4:將棧頂元素出棧,同時用k記錄該物品編號,再將棧中該物品位置的體積置為0,同時加上該物品的體積(t+w[i]),k++;

s5:迴圈進行s2,s3,s4直至k為總物品個數且棧內為空。

接下來,我進一步思考了0-1揹包問題。這就是各物品「選」與「不選」的問題。定義結構體item,設items[i].v是價值,item[i].w是重量;定義二位陣列c[n+1][w+1],c[i][w]表示前i個物品裝入容量為w的揹包時總價值的最大值。則c[i][w]為下述兩者中的較大者:

1、c[i-1][w-物品i的重量]+物品i的價值//當前選擇物品i的情況

2、c[i-1][w]//當前不選擇物品i的情況

在思考問題的初期,在什麼條件下讓物品出棧以及什麼情況下連續出棧這個方面的問題一直困擾著我,使得在**除錯過程中也出了不少差錯,這都是對堆疊這一情形不熟悉所導致。通過設計此演算法,我進一步熟悉掌握堆疊的用法以及回溯演算法,這種資料結構思想在解題的過程中是十分重要的。

#include

#define maxsize 20

int ans=0;//解法的個數

typedef struct

int size[maxsize];

int top;

}seqstack;

void initstack(seqstack &s)

s.top=0;

int i;

for(i=0;is.size[i]=0;

void push(seqstack &s,int x)

if(s.top+1==maxsize)    printf("陣列上溢。\n");

else   

s.size[s.top++]=x;

void pop(seqstack &s)

if(s.top==-1)   printf("陣列下溢。\n");

else    s.top--;

void knap(seqstack &s,int num,int w,int t)

int k=0,i=0,j=1;

do  

while(t>0&&k<=num)  

if(t>=w[k])   

push(s,k);

t-=w[k];   

k++;  

if(t==0)  

printf("\n第%d種挑選方法(",j );

for(i=0;iprintf("%d  ",w[s.size[i]]);   

j++;   

printf(")\n");  

pop(s);//出棧操作  

k=s.size[s.top];

s.size[s.top]=0;  

t+=w[k];  

k++; 

}while(!(s.top==0&&k==num)); 

int main()

seqstacks;

initstack(s);

intt;//揹包體積

printf("請輸入揹包體積:");

scanf("%d",&t);

intnum;//揹包個數

printf("請輸入揹包個數:");

scanf("%d",&num);

intw[maxsize];//揹包內物體體積

printf("請輸入揹包內各個物體體積:\n");

for(int i=0;iscanf("%d",&w[i]);

knap(s,num,w,t);

return0;

0-1揹包問題:

#include

#include

#include

#define nmax 105

#define wmax 10005

#define diagonal 1

#define top 0

using namespace std;

struct item{

int value,weight;

int n,w;

item items[nmax+1];

int c[nmax+1][wmax+1],g[nmax+1][wmax+1];

void compute(int &maxvalue, vector&selection)

for(int w=0;wc[0][w]=0;

g[0][w]=diagonal;//選擇物品i

for(int i=1;i<=n;i++)   c[i][0]=0;

for(int i=1;i<=n;i++)

for(int w=1;wc[i][w]=c[i-1][w];

g[i][w]=top;//不選擇物品i

if(items[i].weight>w)   continue;

if(items[i].value+c[i-1][w-items[i].weight]>c[i-1][w])

c[i][w]=items[i].value+c[i-1][w-items[i].weight];

g[i][w]=diagonal;//不能選擇物品

maxvalue=c[n][w];

selection.clear();

for(int i=n,w=w;i>=1;i--)

if(g[i][w]==diagonal)

selection.push_back(i);

w-=items[i].weight;

reverse(selection.begin(),selection.end());

void input()

cin>>n>>w;

for(int i=1;i<=n;i++)

cin>>items[i].value>>items[i].weight;

int main()

int maxvalue;

vectorselection;

input();

compute(maxvalue,selection);

cout

揹包問題 c 回溯法求解揹包問題

給定n種物品和乙個揹包。物品i的重量是wi,其價值為pi,揹包的容量為c。應如何選擇裝入揹包的物品,使得裝入揹包中物品的總價值最大?根據題目所給的資訊可知就是在wi共有5種物品,即n 5 揹包容量c 30 各物品的重量 wi 5 各物品的價值為 p i 所以所求揹包中的物品的價值最大就是 while...

動態規劃求解揹包問題

假設有n件物品和容量為m的揹包,已知每件物品的重量及價值,在滿足裝入揹包的物品重量最大的前提下,使得裝入物品的總價值最大。1 二維動態規劃 d p i j ma x dp i 1 j dp i 1 j w eigh ts i v alue s i dp i j max dp i 1 j dp i 1...

C 實現揹包問題的求解

假設有乙個能裝入總體積為t的揹包和n件體積分別為w1,w2,的物品,能否從 件物品中挑選若干件恰好裝滿揹包,即使 要求找出所有滿足上述條件的解。例如 當 各件物品的體積 時,可找到下列 組解 可利用回溯法的設計思想來解決揹包問題。首先將物品排成一列,然後順序選取物品裝入揹包,假設已選取了前i件物品之...