回溯法解決0-1背包問題


問題描述:

  有n件物品和一個容量為c的背包。第i件物品的價值是v[i],重量是w[i]。求解將哪些物品裝入背包可使價值總和最大。所謂01背包,表示每一個物品只有一個,要么裝入,要么不裝入。
回溯法

  01背包屬於找最優解問題,用回溯法需要構造解的子集樹。在搜索狀態空間樹時,只要左子節點是可一個可行結點,搜索就進入其左子樹。對於右子樹時,先計算上界函數,以判斷是否將其減去,剪枝啦啦!
   上界函數bound():當前價值cw+剩余容量可容納的最大價值<=當前最優價值bestp。
   為了更好地計算和運用上界函數剪枝,選擇先將物品按照其單位重量價值從大到小排序,此后就按照順序考慮各個物品。

 

#include <stdio.h>
#include <conio.h>

int n;//物品數量
double c;//背包容量
double v[100];//各個物品的價值
double w[100];//各個物品的重量
double cw = 0.0;//當前背包重量
double cp = 0.0;//當前背包中物品價值
double bestp = 0.0;//當前最優價值
double perp[100];//單位物品價值排序后
int order[100];//物品編號
int put[100];//設置是否裝入

//按單位價值排序
void knapsack()
{
    int i,j;
    int temporder = 0;
    double temp = 0.0;

    for(i=1;i<=n;i++)
        perp[i]=v[i]/w[i];
    for(i=1;i<=n-1;i++)
    {
        for(j=i+1;j<=n;j++)
            if(perp[i]<perp[j])//冒泡排序perp[],order[],sortv[],sortw[]
        {
            temp = perp[i];
            perp[i]=perp[i];
            perp[j]=temp;

            temporder=order[i];
            order[i]=order[j];
            order[j]=temporder;
            temp = v[i];
            v[i]=v[j];
            v[j]=temp;

            temp=w[i];
            w[i]=w[j];
            w[j]=temp;
        }
    }
}

//回溯函數
void backtrack(int i)
{
    double bound(int i);
    if(i>n)
    {
        bestp = cp;
        return;
    }
    if(cw+w[i]<=c)
    {
        cw+=w[i];
        cp+=v[i];
        put[i]=1;
        backtrack(i+1);
        cw-=w[i];
        cp-=v[i];
    }
    if(bound(i+1)>bestp)//符合條件搜索右子數
        backtrack(i+1);
}

//計算上界函數
double bound(int i)
{
    double leftw= c-cw;
    double b = cp;
    while(i<=n&&w[i]<=leftw)
    {
        leftw-=w[i];
        b+=v[i];
        i++;
    }
    if(i<=n)
        b+=v[i]/w[i]*leftw;
    return b;

}


int main()
{
    int i;
    printf("請輸入物品的數量和容量:");
    scanf("%d %lf",&n,&c);
    printf("請輸入物品的重量和價值:");
    for(i=1;i<=n;i++)
    {
        printf("第%d個物品的重量:",i);
        scanf("%lf",&w[i]);
        printf("價值是:");
        scanf("%lf",&v[i]);
        order[i]=i;
    }
    knapsack();
    backtrack(1);
    printf("最有價值為:%lf\n",bestp);
    printf("需要裝入的物品編號是:");
    for(i=1;i<=n;i++)
    {
        if(put[i]==1)
            printf("%d ",order[i]);
    }
    return 0;
}

 

時間復雜度分析:

  上界函數bound()需要O(n)時間,在最壞的情況下有O(2^n)個右子結點需要計算上界,回溯算法backtrack需要的計算時間為O(n2^n)

 


免責聲明!

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



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