最优装载问题

题目描述:

现有n个集装箱要装进两艘载重分别为c1,c2的船,其中第i个集装箱重w[i],并且题目保证  i=1nwi≤c1+c2 , 问是否存在一个合理的装载方案,使得这n个集装箱都装进两艘船,若有请找出一种方案。

 

题目分析:

方法1:对于每个集装箱,都有3种状态:装到船1,装到船2,或者不装。按照这个思想我们可以枚举所有的n位3进制数,0,1,2分别代表三种状态,每个数判断是否可以满足题目限制,进而找到合适的方案。因为每次判断需要O(n)的复杂度,位三进制数有3^n个,总的复杂度为O(n*2^n)。

方法2:上面的方法当然也可以搜索来实现,搜索树位一个深度位n的完全三叉树,调用回溯法可以解决。

方法3:题目毕竟只有两艘船,试想如果我让第一艘船尽可能的满载,那么剩下的必然全部放在第二艘船上,就可判断其可行性,那么问题就在于怎么求出第一艘船的最大载重。很容易想到可以用c1作为背包容量,每个物品的w[i]同时作为重量和价值,求个0-1背包的最大价值装载就可以啦。当然也可以想回溯法求背包的最优解,每个物品装不装,2种状态复杂度位2^n.

下面给出回溯法的解决方案。

非递归法解释:

考虑何时回溯:即当继续往下走不会对结果有更新作用的时候既要回溯,回溯到哪里去呢?

回溯到可能对结果仍有贡献为止。

#include <bits/stdc++.h>
using namespace std;
const int maxn = 1000;
int w[maxn],x[maxn],bwx[maxn];
int n,c1,c2,r,cw,bw;

void load(int i)
{
    if(i > n){
        bw = cw;
        for(int i = 1; i <= n; i++) bwx[i] = x[i];
        return;
    }
    r -= w[i];
    if(cw + w[i] <= c1)
    {
        cw+=w[i];
        x[i]=1;
        load(i+1);
        cw-=w[i];
    }
    if(cw + r > bw)
    {
        x[i]=0;
        load(i+1);
    }
    r += w[i];
}

void LOAD()
{
    int i = 1;
    while(true)
    {
        while(i <= n && cw+w[i] <= c1)
        {
            cw+=w[i];
            r -= w[i];
            x[i] = 1;
            i++;
        }
        if(i == n+1)
        {
            bw = cw;
            for(int j = 1; j <= n; j++) bwx[j] = x[j];
        }
        else{
            x[i] = 0;
            r -= w[i];
            i++;
        }
        while(r + cw <= bw)
        {
            while(i > 0 && !x[i])
            {
                i--;
                r+=w[i];
            }
            if(i == 0){
                return;
            }
            x[i] = 0;
            cw -= w[i];
            r  -= w[i];
            i++;
        }
    }
}

int main()
{
    scanf("%d",&n);
    scanf("%d%d",&c1,&c2);
    r = 0,cw = 0,bw = 0;
    for(int i = 1; i <= n; i++)
    {
        scanf("%d",&w[i]);
        r += w[i];
    }
    LOAD();
    //load(1);
    printf("%d\n",bw);
    for(int i = 1; i <= n; i++)
    {
        printf("%d ",bwx[i]);
    }
    puts("");
    return 0;
}
/*
10
500 121
21 54 21 45  1316 65 545 1 20 54
*/

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值