01背包问题的多问题求解

问题描述:给定n种物品和一个背包物品i的重量是wi,其价值为vi,背包最大承重量为C。物品是不可分割的,应如何选择装入背包的物品,使得装入背包的总价值最大?

1.动态规划:

(1)定义一个二维数组dp[i][j],行代表前i个物品,共有n个物品,需要决策n次。每一次决策完成之后,dp[i][j]由之前的决策结果更新而得出来。

(2)如果背包的剩余容量小于物品的重量,那么不装该物品,此时dp[i][j]=dp[i-1][j],也就是最优解即为前i-1个物品的最优解。

(3)如果背包容量可以装下物品,此时需要决策是否装入该物品,利用max函数决策dp[i][j]=max(dp[i-1][j],dp[i-1][j-w[i]]+v[i]),进而取得最大价值。

代码展示如下:

//动态规划 

int DP(){

  

   for(int i = 1; i <= N; i++)

       for(int j = 0; j <= C; j++)

        {

             //i物品装不下,则此时的最优解与i-1的相同

            if(j<w[i])

              dp[i][j] = dp[i - 1][j];

              else //判断是否要装i物品 

                dp[i][j] = max(dp[i-1][j], dp[i - 1][j - w[i]] + v[i]);

    }   

   return dp[N][C];

}

2.回溯

深度优先从根节点开始,首先定义当前的最大价值now_v和当前总重量now_w。如果从根节点到下一个子节点加上物品的重量后背包还有剩余,调用递归函数继续向下搜索。如果到达了最下面一层,然后进行最优价值与当前最大价值进行判断。进而获得最优解。

代码如下:

//回溯法

int LookBack(int i,int now_w,int now_v){

   int j;

   if(i>N){

       if(now_v>mostvalue)

          mostvalue=now_v;

   }else{

          if(now_w+w[i]<=C){

              now_w+=w[i];

              now_v+=v[i];

              LookBack(i+1,now_w,now_v);

                 //从背包里取出物品进行回溯

              now_w-=w[i];

              now_v-=v[i];

          }

              LookBack(i+1,now_w,now_v);

   }

   return mostvalue;

}

3.分支限界

广度优先,定义当前的最大价值now_v和当前总重量now_w,建立约束条件,now_w<=c以及限界条件now_v+r[i+1]>bestvalue。其中r[i]是计算当前节点后面所有节点的重量总和用于剪枝。若符合则进行递归调用否则返回最优解。

代码如下:

//分支限界法

int BoundBack(int i,int now_w,int now_v){

   int j;

   if(i>N){

       if(now_v>bestvalue)

          bestvalue=now_v;

   }else{

       for(j=0;j<=1;j++){

          now_v+=v[i]*j;

          now_w+=w[i]*j;

       if(now_w<=C&&now_v+r[i+1]>bestvalue)

          BoundBack(i+1,now_w,now_v);

       }

   }

   return bestvalue;

}

4.贪心

首先创建排序函数对物品的单价进行排序,大的优先,然后通过调用排序函数获得新的物品顺序,然后按照顺序用当前价值value+=v[i]来更新当前价值,用背包容量依次减去w[i],直到下一个物品重量大于剩余容量,最终获得最优解。

代码如下:

//贪心算法

void Sort(float w1[],float v1[]){

   float temp1,temp2;

   for(int i=1;i<=N;i++){

       for(int j=i;j<=C;j++){

          temp1=v1[i]/w1[i];

          temp2=v1[j]/w1[j];

          if(temp1<temp2){

              swap(w1[j],w1[i]);

              swap(v1[j],v1[i]);

          }

       }

   }

}

int tanxin(int m){

   float w1[N],v1[N];

   int value;

   for(int i=1;i<=N;i++)

   {

       v1[i]=rand()%5+1;

       w1[i]=rand()%5+1;

       cout<<"物品"<<i<<",价值"<<v1[i]<<",重量"<<w1[i]<<endl;

   }

   Sort(w1,v1);

   for(int i=1;i<=N;i++){

       //cout<<"排序后的物品"<<i<<",价值"<<v1[i]<<",重量"<<w1[i]<<endl;

   }

   for(int i=1;i<=N;i++){

       if(w1[i]<=m){

          m-=w1[i];

          value+=v1[i];

       }else{

          return value;

       }

   }

   return value;

}

复杂度分析:

1.动态规划:时间复杂度:O(n*c),c为背包容量,嵌套两次for循环,从开始到结束最多需要执行n*c次。

空间复杂度:O(n*c),建立了一个dp[n][c]二维数组,空间占用为n*c。

2.回溯法:时间复杂度:O(2^n),使用了二叉树,共有n层,需要搜索2^n个节点,所以最多需要执行2^n次。

空间复杂度: O(2^n),二叉树有2^n个节点。

3.分支限界法:时间复杂度:O(n*2^n),最多需要遍历到第n层,还有限界函数O(n)判断,所以最多共n*2^n次。

空间复杂度:O(2^n),二叉树有2^n个节点。

4.贪心法:时间复杂度:O(n),一层for循环,执行了n次。

空间复杂度:O(n),排序函数将n个物品进行了排序,占用了包含n个数据的一维数组。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值