1. 程式人生 > >揹包問題的遞迴和非遞迴演算法

揹包問題的遞迴和非遞迴演算法

/**
簡單揹包問題
問題定義:
有一個揹包重量是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;
}