01背包問題(回溯算法實現)


    問題描述:有n件物品和一個容量為c的背包。第i件物品的價值是v[i],重量是w[i]。求解將哪些物品裝入背包可使價值總和最大。所謂01背包,表示每一個物品只有一個,要么裝入,要么不裝入。
    今 天下午的算法復習課,老師提的各種算法經典問題時,出現頻率就是01背包問題了!動態規划、回溯法、分支限界法,在貪心算法時也提到注意背包問題,當然 01背包問題不能用貪心算法實現,不能保證能得到最優解。回溯法是最近學的,所以試着用C語言將其實現了下,下面作以分析,后期將會繼續用其他兩種算法實 現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()
{
    inti,j;
    inttemporder = 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)
{
    doublebound(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)
{
    doubleleftw= 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;
    returnb;

}


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]);
    }
    return0;
}
運行結果截圖:



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

go to sleep......
       


免責聲明!

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



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