回溯算法之轮船货箱装载问题(最优装载)

1. 问题描述:

  给定 n 个货箱,货箱 i 重为 wi ,船可以装载的货箱总重量为W。货箱装载问题是在不使船翻的前提下装载尽可能多的货箱。

2. 解空间:

  假设解可以由向量 (x1, x2, ... , xn) 表示, xi 属于 {0, 1} , xi = 1 表示货箱 i 被装上船, xi = 0 表示货箱 i 不装上船。

3. 约束函数:

  令 cw(i) 表示到第 i 层的当前总重量,则约束函数为 C(i) = cw(i-1) + wi 

4. 限界函数:

  B(i) = C(i) + r(i),其中 r(i) 表示剩余货箱的总重量

5. 剪枝:

  若 B(i) <= bestw,则停止搜索第 i 层及其下面的层,否则继续搜索。其中,bestw 表示目前为止得到的最佳重量。

 

C 语言实现:

代码参考自:https://www.xuebuyuan.com/2133367.html

// JJU_干干

/*
++++++++++++++++++++++++++++++++++++++++++++++++++++

一般解题步骤分为三步
一:针对所给问题,定义问题的解空间
二:确定易于搜索的解空间结构(一般为子集树或者排列树)
三:以深度优先的方式搜索解空间,并且在搜索过程中用减枝函数避免无效搜索
其中子集树就是选一部分,比如0-1背包问题,装载问题,
而排列树就是选所有,只是顺序不一样,例如旅行商(邮递员)问题

++++++++++++++++++++++++++++++++++++++++++++++++++++
*/

#include <stdio.h>
#include <stdlib.h>
#define NUM 100
int n;  //集装箱数量
int W;  //轮船载重量
int w[NUM]; //集装箱重量
int x[NUM]; //当前搜索的解向量
int r;  //剩余集装箱重量
int cw; //当前轮船载重量
int bestw;  //最优载重
int bestx[NUM]; //最优解向量

void init()
{
    int i;
    bestw=0;  // 最优载重量初始化为0
    printf("请输入各集装箱的重量:");
    for( i=1;i<=n;i++)
    {
        scanf("%d",&w[i]);
        r+=w[i];
    }
}

//形参代表搜索第t层节点,从1开始
void BackTrack(int t)
{
    int i;
    //到达叶子节点, 此时就找到了一个可行解,然后就更新最优解向量和最优载重
    if(t>n)  
    {
        for(i=1;i<=n;i++)
        {
            bestx[i]=x[i];
        }
        bestw=cw;
    }
    else  // 未到达叶子节点
    {
        r-=w[t];    //先假设当前货箱可以装入,再判断能不能装入,因此这里要更新剩余集装箱重量
 
        //没有超出载重量
        if(cw+w[t]<=W) 
        {
            x[t]=1;  // 可以装入,则赋值为1
            cw+=w[t];  // 更新当前装入的总重量
            BackTrack(t+1);  // 递归进入下一货箱, 只要不超在,就会一直深度搜索下去
            cw-=w[t];   //返回时要将当前载重量还原,即回溯
        }
    
        //如果当前载重量加上剩余集装箱的重量没有超过前一个最优装载量的话就不用考虑了
        //此处即为剪枝函数,剪枝之前,至少有一个可行解已经产生
        if(cw+r>bestw)      
        {
            x[t]=0;     // 如果进入这个if 语句就说明,以该节点的子树不能剪,但通过上一个if语句的回溯后,该节点进入另一个选择,应赋值为0
            BackTrack(t+1);  //递归进入下一货箱
        }
        r+=w[t];    //返回时还原剩余集装箱重量,即回溯
    }
    return;
}

void print() // 打印函数
{
    int i;
    printf("最优载重:%d\n",bestw);
    printf("最佳解向量为:");
    for(i=1;i<=n;i++)
        printf("%d",bestx[i]);
    printf("\n即:");
    for(i=1;i<=n;i++)
    {
        if(bestx[i]==1)
            printf("\n第%d个货箱(重量为:%d), 装入轮船",i,w[i]);
        else
            printf("\n第%d个货箱(重量为:%d), 不装入轮船",i,w[i]);
    }
    printf("\n");
}

void main()
{
    printf("请输入集装箱的个数:");
    scanf("%d",&n);
    printf("请输入轮船的载重量:");
    scanf("%d",&W);
    init();
    BackTrack(1);
    print();
    system("pause");
}

 

运行结果:

回溯算法之轮船货箱装载问题(最优装载)

 

Python 实现:

# JJU_干干

def init():
    global bestw,cw,w,r,x
    bestw =0
    cw = 0
    x=[0]*100
    w= list(eval(input("请输入各货箱的重量:")))
    r = sum(w)


def BackTrack(t):
    global bestx,bestw,cw,r,x
    if t>(n-1):
        bestx = x
        bestw = cw
    else:
        r -=w[t]
        if (cw+w[t])<=W:
            cw += w[t]
            x[t] = 1
            BackTrack(t+1)
            cw -= w[t]

        if (cw+r) > bestw:
            x[t] = 0
            BackTrack(t+1)

        r += w[t]
    return

def print_func():
    global bestw,bestx,w,x,n
    print("最佳载重为:{0},最佳解向量:{1}".format(bestw,bestx[0:n]))
    print("即:")
    for i in range(n):
        if x[i] == 1:
            print("第{0}个货箱(重量为:{1})装入轮船".format(i+1,w[i]))
        else:
            print("第{0}个货箱(重量为:{1})不装入轮船".format(i + 1, w[i]))


if __name__ == '__main__':
    n = int(input("请输入货箱的个数:"))
    W = int(input("请输入轮船的载重量:"))
    init()
    BackTrack(0)
    print_func()

 

运行结果:

回溯算法之轮船货箱装载问题(最优装载)

 

上一篇:微信小程序等比例缩小图片(用于图片内容合规验证尺寸不能超过750*1334)


下一篇:python+openCV图像处理(十七)