分支限界法实现最优装载c++_经典问题-回溯法-装载问题

写在前面:感谢飞哥提出的问题,让我认识到自己对算法课本的忽视。这可是基础呀,从现在开始补吧!!!文章内容不免有错误和不足之处,欢迎大家指出,共同学习。

1 相关知识点

1.1什么是回溯法

基本思想:把问题的解空间转化为图或者树的结构,然后使用深度优先搜索策略进行遍历,再遍历的过程中记录和寻找所有可行解或者最优解;

1.2什么是子集树

所给的问题是从n个元素的集合S中找出满足某种性质的子集时,相应的解空间称为子集树;如01背包;
因为要处理所有的情况,子集树的复杂度是O(2^n);
如果需要记录子集树的最优解的选择,复杂度为O(n2^n);具体分析看下面的问题;

2 装载问题引入

2.1问题描述

n个集装箱装到2艘载重量为C1和C2的轮船A和B上,其中集装箱的重量为wi,且all(wi)<=c1+c2;
问是否有合适的装载方案,将n个集装箱放到这两艘轮船上;有找出方案。

2.2最优策略

A和B是相互独立的,即如何在A或者B上,放置物品是互不干扰的。
但是假如先在A上放置货物,则因剩下的货物不同,会使B上装载的货物的情况发生变化。因此,为了保证B上装载的情况,满足A和B将所有的货物都装下,A上应该尽可能的多放物品。

2.3 代码实现

/*
 * @Author: cy
 * @Date: 2020-05-10 21:52:29
 * @LastEditors: cy
 * @LastEditTime: 2020-05-10 22:25:37
 * @FilePath: FromBook装载问题.cpp
 * @Tags: 
 */
#include<cstdio>
using namespace std;
const int num=100;
int n,c1,c2,w[num];// n个集装箱,A,B货轮载重量分别为C1,C2,W[i],第i个集装箱的重量
int cw,bw,rw;//cw,当前集装箱货物重量;bw,最优载重重量,rw,剩余集装箱重量;
int x[num],bx[num];//x[],A货轮的当前结果;bx[],A货轮的最优结果;
void BackTrack(int i) 
{
    //处理完了前n个集装箱;
    if(i>n){
        if(cw>bw){//cw,目前A中装了cw重量的集装箱;
        //更新最优解;
            bw=cw;
            for(int i=1;i<=n;i++) bx[i]=x[i];
        }    
        return;
    }
    //rw表示处理完第i个之后(选或不选),还剩下rw-w[i]重量的集装箱未处理;
    rw-=w[i];

    if(cw+w[i]<=c1){//cw,第i个货箱之前的重量 + 第i个货箱小于A的最大重量C1;
        cw+=w[i];//加上
        x[i]=1;//标记i被选
        BackTrack(i+1);
        cw-=w[i];//减去重量
        x[i]=0;//撤销标记;
    }
    //不选择第i个物品的话;
    //if cw:表示[1:i)的数据  rw:表示(i,n]的数据 ,不包括第i个的数据
    //如果不包括第i的数据的和(cw+rw) 大于  目前最优解bw,则可以递归下去;
    if(cw+rw > bw){
        x[i]=0;
        BackTrack(i+1);
    }

    //处理完第i个物品当前的情况了;
    //因为再上一层,有两种情况;
    //1;选择第i物品;
    //2:不选择第i个物品
    //如果目前处理的是上一层第1种情况,那么我们就有必要加上这个w[i];
    //否则会影响上一层处理第2种情况;
    rw+=w[i];
    return ;
}
int main()
{
    //读入数据;
    scanf("%d%d%d",&n,&c1,&c2);
    for(int i=1;i<=n;i++) 
    {
        scanf("%d",&w[i]);
        rw+=w[i];//rw表示目前最优集装箱的剩余重量;
    }

    //递归回溯
    BackTrack(1);

    //bw表示A货轮装下的货物重量;剩余的重量 > B可以放下的最多,则不可;
    if(rw-bw>c2){
        printf("没有装载方案n");
    }else{
        printf("货轮A:n");
        for(int i=1;i<=n;i++) if(bx[i]) printf("%d ",i);
        printf("n货轮B:n");
        for(int i=1;i<=n;i++) if(0==bx[i]) printf("%d ",i);
    }
    return 0;   
}

参考博客

装载问题(回朔法) - outthinker - 博客园​www.cnblogs.com
e204cfef854d70204358e2a4651ce71f.png
装载问题(最优装载问题变形)-回溯法-深度搜索_C/C++_zzzsdust的博客-CSDN博客​blog.csdn.net
3af84f663792d6f07c83dd40fe94e208.png
#include #include #include #include using namespace std; ifstream infile; ofstream outfile; class Node { friend int func(int*, int, int, int*); public: int ID; double weight;//物品的重量 }; bool comp1(Node a, Node b) //定义比较规则 { return a.weight > b.weight; } class Load; class bbnode; class Current { friend Load; friend struct Comp2; private: int upweight;//重量上界 int weight;//结点相应的重量 int level;//活结点在子集树中所处的层次 bbnode* ptr;//指向活结点在子集树中相应结点的指针 }; struct Comp2 { bool operator () (Current *x, Current *y) { return x->upweightupweight; } }; class Load { friend int func(int*, int, int, int*); public: int Max0(); private: priority_queue<Current*, vector, Comp2>H;//利用优先队列(最大堆)储存 int limit(int i); void AddLiveNode(int up, int cw, bool ch, int level); bbnode *P;//指向扩展结点的指针 int c;//背包的容量 int n;//物品的数目 int *w;//重量数组 int cw;//当前装载量 int *bestx;//最优解方案数组 }; class bbnode { friend Load; friend int func( int*, int, int, int*); bbnode* parent; bool lchild; }; //结点中有双亲指针以及左儿子标志 int Load::limit(int i) //计算结点所相应重量的上界 { int left,a; left= c - cw;//剩余容量 a = cw; //b是重量上界,初始值为已经得到的重量 while (i <= n && w[i] parent = P; b->lchild = ch; Current* N = new Current; N->upweight = up; N->weight = cw; N->level = level; N->ptr = b; H.push(N); } int Load::Max0() { int i = 1; P = 0; cw = 0; int bestw = 0; int up = limit(1); while (i != n + 1) { int wt = cw + w[i]; //检查当前扩展结点的左儿子结点 if (wt bestw) bestw =wt; AddLiveNode(up,wt, true, i + 1); } up = limit(i + 1); //检查当前扩展结点的右儿子结点 if (up >= bestw)//如果右儿子可行 { AddLiveNode(up,cw, false, i + 1); } Current* N = H.top(); //取队头元素 H.pop(); P = N->ptr; cw = N->weight; up = N->upweight; i = N->level; } bestx = new int[n + 1]; for (int j = n; j > 0; --j) { bestx[j] = P->lchild; P = P->parent; } return cw; } int func(int *w, int c, int n, int *bestx) //调用Max0函数对子集树的优先队列式进行分支限界搜索 { int W = 0; //初始化装载的总质量为0 Node* Q = new Node[n]; for (int i = 0; i < n; ++i) { Q[i].ID = i + 1; Q[i].weight = w[i+1]; W += w[i+1]; } if (W <= c)//如果足够装,全部装入 return W; sort(Q, Q + n, comp1); //首先,将各物品按照重量从大到小进行排序; Load K; K.w = new int[n + 1]; for (int j = 0; j < n; j++) K.w[j + 1] = w[Q[j].ID]; K.cw = 0; K.c = c; K.n = n; int bestp = K.Max0(); for (int k = 0; k < n; k++) { bestx[Q[k].ID] = K.bestx[k + 1]; } delete []Q; delete []K.w; delete []K.bestx; return bestp; } int main() { int*w,*Final; int c,n,i,best; infile.open("input.txt",ios::in); if(!infile) { cerr<<"open error"<>c; infile>>n; w=new int[n+1]; for(i=1;i>w[i]; infile.close(); Final = new int[n+1]; best = func( w, c, n, Final); outfile.open("output.txt",ios::out); if(!outfile) { cerr<<"open error"<<endl; exit(1); } outfile << best << endl; for (int i = 1; i <= n; ++i) { outfile<<Final[i]<<" "; } outfile.close(); return 0; }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值