回溯法-子集树-装载问题

问题描述:有一批共n个集装箱要装上2艘载重量分别为c1和c2的轮船,其中集装箱i的重量是wi,且不能超过轮船总载重量。即Σwi<=c1+c2。若集装箱能全部装载则输出YES, 否则输出为NO,n为0时结束输入。

问题模型:解子集空间树。

解决思路:首先将第一艘轮船尽可能的装满。然后将剩余的集装箱装上第二艘轮船。将第一艘轮船尽可能的装满等价于选取全体集装箱的一个子集,使该子集中集装箱重量之和最接近c1。第一艘船装载完后,根据剩余的集装箱重量与c2轮船载重量比较。若剩余的集装箱重量<=c2则能装入到两艘船。(求出不超过c1的最大值max,若集装箱总重量-max<=c2则能装入到两艘船)

c1最大装载代码:

void backpack(int t){
     if(t>n){
         if(cw>bestw){
             bestw = cw;
        }
     }else{
          for(int j = 0;j<=1;j++){        //枚举物体t所有可能的路径,
                x[t] = j;
            if(cw+w[t]*x[t]<=c1){       //约束为当前集装箱的重量不能超过c1剩余重量
                 cw+=w[t]*x[t];
                 backpack(t+1);
                 cw-=w[t]*x[t];
            }
         }
     }
}
完整代码:
/**
   @回溯-装载问题
   @求出不超过c1的最大值max,若物体总重量-max<=c2则能装入到两艘船。
*/
#include<iostream>
#include<algorithm>
#define MAX 100
using namespace std;
int n;                    //物品个数
int c1,c2;               //轮船载重
int w[MAX];             //物品重量
int x[MAX];            //物品选择
int cw = 0;           //当前装载量
int bestw = 0;       //最优装载
void backpack(int t){
     if(t>n){
         if(cw>bestw){
             bestw = cw;
        }
     }else{
          for(int j = 0;j<=1;j++){     //枚举物体t所有可能的路径,
                x[t] = j;
            if(cw+w[t]*x[t]<=c1){     //约束为当前集装箱的重量不能超过c1剩余重量
                 cw+=w[t]*x[t];
                 backpack(t+1);
                 cw-=w[t]*x[t];
            }
         }
     }
}
int main(){
     cin>>c1>>c2>>n;    //轮船c1,c2最大载重量以及集装箱数量
     while(n){
       int k = 1;
       int sum = 0;    //集装箱总重量
       for(int i = 1;i<=n;i++){
          cin>>w[k];
          sum+=w[k];
          k++;
       }
       backpack(1);
       if(sum-bestw<=c2){     //集装箱剩余重量与c2轮船载重量比较
           cout<<"YES"<<endl;
       }else{
           cout<<"NO"<<endl;
       }
         bestw = 0;                //bestw复位0
         cin>>c1>>c2>>n;
    }
       return 0;
}
/**
7 8 2
8 7

7 8 2
8 8

7 8 3
3 3 8

0 0 0
*/



猜你喜欢

转载自blog.csdn.net/lfb637/article/details/80737627