程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> C++入門知識 >> 回溯算法---01背包問題

回溯算法---01背包問題

編輯:C++入門知識


背包問題:
         給定n種物品(每種物品僅有一件)和一個背包。物品i的重量是wi ,其價值為pi ,背包的容量為w。問應如何選擇物品裝入背包,使得裝入背包中的物品的總價值最大?
            l  如果在裝入背包時,物品可以切割,即可以只裝入一部分,這種情況下的問題稱為背包問題。
            l  在裝入背包時,每種物品i只有兩種選擇,裝入或者不裝入,既不能裝入多次,也不能只裝入一部分。因此,此問題稱為0-1背包問題。
        要想得到最優解,就要在效益增長和背包容量消耗兩者之間尋找平衡。也就是說,總應該把那些單位效益最高的物體先放入背包。

背包問題可看做是一種回溯:
          每個包是一個節點, 節點共有2個候選值0、1 。 0代表不放人背包中, 1代表放入背包中。

 


      因此,背包問題就轉換為找到滿足條件的路徑問題。

因此,可用回溯方法解決。


回溯方法解決背包問題:

方法一:

// 判斷節點(I,j)是否為解路徑上的節點,其中:
// 
//   i表示解路徑上的第i個測試節點、j表示該節點的某個候選值
//   A[i] 保存第i個節點選用的值
 
BOOL TestNode(I ,j)
{
   更新相關參數值(假定選擇了此候選值j,因此更新受影響的參數值);
 
   與0—(i-1) 層進行判斷,看是否與以遍歷的節點有沖突
 
   若有沖突, 則返回FALSE;
   若無沖突, 則 將節點i的值j,保存到對應的數組中A[I]=J;
 
   判斷I是否為最後一層,
  若是最後一層,則成功找到一條解路徑,返回TRUE;
 
  若不是最後一層,則判斷第i+1層是否有正確的節點。
 
   BOOL bFlag=FALSE;
   FOR(k=0;k< CANDIDATA_NUM;k++) //候選值【0,。。。,CANDIDATA_NUM -1】
   {
        If(TestNode(i+1,k))
        {
           找到一個解;
           bFlag=True;
        }
        //不管TestNode(i+1,k)是成功的還是失敗的,退出後,都要對參數進行還原
       還原相關參數值(撤銷了候選值k,因此要還原受影響的參數值);
 
   }
 
   RETURN bFlag;
 
}
 
[cpp] 
int m,n=5,x[10]={0}; 
int w[6]={0,2,2,6,5,5},v[6]={0,6,3,5,4,6}; 
int c=10; 
int cw=0,cv=0,bestv=0; 
 
BOOL TestNode(int i,int j){  // 第 i個物品的 候選值為0和1 
 
 
    //更新相關參數值 
    cw+=w[i]*j; 
    cv+=v[i]*j; 
 
    //與之前的物品相比  有無沖突 
    if (cw>c) 
        return FALSE; 
 
    //無沖突,添加至解路徑 
    x[i]=j; 
 
    // 到此為止  0--i行 均無沖突 
    // 如果是最後一行  成功找到一個解 
    if(i==n){ 
 
        for(i=1;i<=n;i++) 
            printf("%d",x[i]); 
        if(cv>bestv) 
            bestv=cv; 
        printf("\n"); 
        return TRUE; 
 
    } 
 
    //如果不是最後一行 則判斷i+1行 
 
    BOOL bSuit=FALSE; 
    for (int k=0;k<=1;k++) 
    {        
        //第i+1行存在合適位置  
        if (TestNode(i+1,k)) 
            bSuit=TRUE;  
 
        //還原相關參數 
        cw-=w[i+1]*k; 
        cv-=v[i+1]*k; 
    } 
 
   return bSuit; 
 

 
 
 
void  Bag() 

 
    for (int i=0;i<=1;i++) 
    { 
        TestNode(1,i); 
    } 
 
 
 

或 不更新與還原相關變量, 而是根據已知信息推導相關變量值

[cpp] 
int m,n=5,x[10]={0}; 
int w[6]={0,2,2,6,5,5},v[6]={0,6,3,5,4,6}; 
int c=10; 
int cw=0,cv=0,bestv=0; 
 
BOOL TestNode(int i,int j){  // 第 i個物品的 候選值為0和1 
 
 
 
   //根據已知 推倒相關參數值 
    cw=0; 
    cv=0; 
    for (int k=1;k<=i-1;k++) 
    { 
        cw+=x[k]*w[k]; 
        cv+=x[k]*v[k]; 
    } 
 
    cw+=w[i]*j; 
    cv+=v[i]*j; 
 
 
 
    //與之前的物品相比  有無沖突 
    if (cw>c) 
        return FALSE; 
 
    //無沖突,添加至解路徑 
    x[i]=j; 
 
    // 到此為止  0--i行 均無沖突 
    // 如果是最後一行  成功找到一個解 
    if(i==n){ 
 
        for(i=1;i<=n;i++) 
            printf("%d",x[i]); 
        if(cv>bestv) 
            bestv=cv; 
        printf("\n"); 
        return TRUE; 
 
    } 
 
    //如果不是最後一行 則判斷i+1行 
 
    BOOL bSuit=FALSE; 
    for (int k=0;k<=1;k++) 
    {        
        //第i+1行存在合適位置  
        if (TestNode(i+1,k)) 
            bSuit=TRUE;  
    } 
 
   return bSuit; 
 

 
 
 
void  Bag() 

 
    for (int i=0;i<=1;i++) 
    { 
        TestNode(1,i); 
    } 
 
 
 


方法二:
       根據背包問題的特殊性 ,可簡化算法

 


[cpp] 
int m,n=5,x[10]={0}; 
int w[6]={0,2,2,6,5,5},v[6]={0,6,3,5,4,6}; 
int c=10; 
int cw=0,cv=0,bestv=0; 
 
int ok(int k) 

    int u=1; 
    if(cw>c) 
        u=0; 
    return u; 

 
 
int f(int k) 

    int i; 
    if (k>n) 
    { 
        for(i=1;i<=n;i++) 
            printf("%d",x[i]); 
        if(cv>bestv) 
            bestv=cv; 
        printf("\n"); 
    } 
    else 
    { 
        x[k]=1;    //判斷候選值1 
        cw+=w[k]; 
        cv+=v[k]; 
        if(ok(k)) 
            f(k+1); 
        cw-=w[k];  //判斷候選值0 
        cv-=v[k]; 
        x[k]=0;   
        if(ok(k)) 
            f(k+1); 
    } 
 
    return k; 

作者:shuilan0066
 

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved