分支限界法 0-1背包問題-隊列式


一.分支限界法概述

(1)分支限界法就是采用廣度優先的策略,依次搜索活結點所有的分枝,也就額是所有的相鄰結點。在求最優解時采用一個限界函數,計算限界函數值,選擇一個最有利的子節點作為擴展結點,使搜索樹朝着解空間樹上有最優解的分支推進,以便盡快找出一個最優解。

(2)常見的兩種分支限界法

  先進先出(FIFO)隊列式:在先進先出的分支限界法中,用隊列作為組織活結點表的數據結構,並按照隊列先進先出的原則選擇結點作為擴展結點。

  優先隊列(PQ):用優先隊列作為組織活結點表的數據結構。

二.0-1背包問題

 問題:給定n種物品和一背包。物品i的重量是wi,其價值為pi,背包的容量為C。問應如何選擇裝入背包的物品,使得裝入背包中物品的總價值最大?

#include<iostream>
#include<queue>
using namespace std;

const int maxn=99; 
int n,c;
int w[maxn];
int v[maxn];

int bestv=0;
int bestx[maxn];
int total=1;        //解空間中的節點數累計,全局變量 
struct nodetype        //隊列中的結點類型
{
    int no;            //結點編號,從1開始 
    int i;            //當前結點在搜索空間中的層次 
    int w;            //當前結點的總重量 
    int v;            //當前結點的總價值 
    int x[maxn];    //當前結點包含的解向量 
    double ub;        //上界 
};

void input()
{
    cout<<"請輸入物品的個數:"<<endl;
    cin>>n;
    cout<<"請輸入每個物品的重量及價值(如5 4):"<<endl;
    for(int i = 1; i <= n; i++)
    {
        cin>>w[i]>>v[i];
    }
    cout<<"請輸入背包的容量:"<<endl;
    cin>>c;
}

void bound(nodetype &e)        //計算分支結點e的上界 
{
    int i=e.i+1;        //考慮結點e的余下物品
    int sumw=e.w;
    double sumv=e.v;
    while((sumw+w[i]<=c)&&i<=n) 
    {
        sumw+=w[i];
        sumv+=v[i];
        i++;
    }
    if(i<=n)            //余下物品只能部分裝入 
    e.ub=sumv+(c-sumw)*v[i]/w[i];
    else e.ub=sumv; 
} 

void enqueue(nodetype e,queue<nodetype> &qu)
//結點e進隊qu 
{
    if(e.i==n)                //到達葉子節點,不在擴展對應一個解 
    {
        if(e.v>bestv)        //找到更大價值的解 
        {
            bestv=e.v;
            for(int j=1;j<=n;j++)
            bestx[j]=e.x[j];
        }
    }
    else qu.push(e);        //非葉子結點進隊
} 

void bfs()
{
    int j;
    nodetype e,e1,e2;
    queue<nodetype> qu;
    
    e.i=0;
    e.w=0;
    e.v=0;
    e.no=total++;
    
    for(j=1;j<=n;j++)
    e.x[j]=0;
    bound(e);
    qu.push(e);
    
    while(!qu.empty())
    {
        e=qu.front();qu.pop();    //出隊結點e 
        if(e.w+w[e.i+1]<=c)        //剪枝,檢查左孩子結點 
        {
            e1.no=total++;        //建立左孩子結點 
            e1.i=e.i+1;
            e1.w=e.w+w[e1.i];
            e1.v=e.v+v[e1.i];
            for(j=1;j<=n;j++)
            e1.x[j]=e.x[j];
            e1.x[e1.i]=1;
            bound(e1);        //求左孩子的上界 
            enqueue(e1,qu);    //左孩子結點進隊 
        }
        e2.no=total++;
        e2.i=e.i+1;
        e2.w=e.w;
        e2.v=e.v; 
        for(j=1;j<=n;j++)
            e2.x[j]=e.x[j];
        e2.x[e2.i]=0;
        bound(e2);
        if(e2.ub>bestv)        //若右孩子結點可行,則進隊,否則被剪枝 
        enqueue(e2,qu);    
    }
} 

void output()
{
    cout<<"最優值是:"<<bestv<<endl;
    cout<<"(";
    for(int i=1;i<=n;i++)
        cout<<bestx[i]<<" ";
    cout<<")";
}

int main()
{
    input();
    bfs();
    output();
    return 0;
 } 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM