有两艘船需要装运的n箱货物,第一艘船的载重量是c1,第二艘船的载重量是c2,wi是货箱i的重量,且w1+w2+……+wn<=c1+c2

(1) 问题描述: 
       有两艘船和需要装运的n个货箱,第一艘船的载重量是c1,第二艘船的载重量是c2,wi是货箱的质量,且w1+w2+...+wn <= c1+c2. 
希望确定是否有一种可将所有n个货箱全部装船的方法。若有的话,找出该方法。

(2) 举例描述: 
       当n=3,c1=c2=50,w=[10,40,40]时,可将货箱1、2装到第一艘船上,货箱3装到第二艘船上。 
       但是如果w=[20,40,40],则无法将货箱全部装船。由此可知问题可能有解,可能无解,也可能有多解。 
   
(3) 问题分析 
       虽然是关于两艘船的问题,其实只讨论一艘船的最大装载问题即可。因为当第一艘船的最大装载为bestw时, 
若w1+w2+...+wn-bestw <= c2,则可以确定一种解,否则问题就无解。这样的问题转化为第一艘船的最大装载问题。

(4) 算法设计 
       转化为一艘船的最优化问题后, 问题的解空间为一个子集树。也就是算法要考虑所有物品取、舍情况的组合, 
n个物品的取舍组合共有2的n次方个分支。

1> 和回溯算法的思想一样,用FIFO分支搜索所有的分支,并记录已搜索分支的最优解,搜索完子集树也就找出了问题的解。 
   
       2> 要想求出最优解,必须搜索到叶结点,所以要记录数的层次。当层次为n+1时,搜索完全部叶结点,算法结束。

3> 分支搜索过程中活结点的"层"是需要标识的,否则入队后无法识别结点所在的层。每处理完一层让"-1"入队,以此来标识 
          "层",并用变量i来记录当前层。

4> 每个活结点要记录当前船的装载量。

代码示例:

  1 <?php
  2 class Queue
  3 {
  4     private $queue = array();
  5
  6     /**
  7      * 入栈
  8      */
  9     public function push($val)
 10     {
 11         array_push($this->queue, $val);
 12     }
 13
 14     /**
 15      * 出栈
 16      */
 17     public function pop()
 18     {
 19         $value = array_shift($this->queue);
 20         return $value;
 21     }
 22
 23     /**
 24      * 判断是否为空栈
 25      */
 26     public function is_empty()
 27     {
 28         if(empty($this->queue))
 29         {
 30             return true;
 31         } else {
 32             return false;
 33         }
 34     }
 35 }
 36
 37 class BranchLimitFIFOSearch
 38 {
 39     private $n;//n个货箱
 40     private $c1;//第一艘船的载重量
 41     private $c2;//第二艘船的载重量
 42     private $bestw;//第一艘船的最大载量
 43     private $ew = 0;//当前船的装载量
 44     private $w;//货箱质量数组 array
 45     private $s = 0;//所有货箱的重量之后
 46     private $queue;//FIFO队列
 47
 48     /**
 49      * 构造函数
 50      */
 51     public function __construct($n, $c1, $c2, $w)
 52     {
 53         $this->n = $n;
 54         $this->c1 = $c1;
 55         $this->c2 = $c2;
 56         $this->w = $w;
 57         $this->s = is_array($w) ? array_sum($w) : 0;
 58         $this->queue = new Queue();
 59     }
 60
 61     /**
 62      * 最忧装载值
 63      * @param $c 第一艘船的载重量
 64      */
 65     public function max_loading($c)
 66     {
 67         $i = 1;//E-节点的层
 68         $this->ew = 0;//当前船的装载量
 69         $this->bestw = 0;//目前的最优值
 70         $this->queue->push(-1);
 71
 72         while(!$this->queue->is_empty())//搜索子集空间树
 73         {
 74             if($this->ew + $this->w[$i] <= $c)//检查E-节点的左孩子,货箱i是否可以装载
 75             {
 76                 $this->add_live_node($this->ew + $this->w[$i], $i);//货箱i可以装载
 77             }
 78             $this->add_live_node($this->ew, $i);
 79
 80             $this->ew = $this->queue->pop();//取下一个节点
 81
 82             if(-1 == $this->ew)
 83             {
 84                 if($this->queue->is_empty())
 85                 {
 86                     break;
 87                 }
 88                 $this->queue->push(-1);
 89                 $this->ew = $this->queue->pop();//取下一个节点
 90                 $i++;
 91             }
 92         }
 93
 94         return $this->bestw;
 95     }
 96
 97     /**
 98      * 入队
 99      */
100     public function add_live_node($wt, $i)
101     {
102         if($this->n == $i)//是叶子
103         {
104             $this->bestw < $wt && $this->bestw = $wt;
105         } else {//不是叶子
106             $this->queue->push($wt);
107         }
108     }
109
110     /**
111      * 所有货箱的重量
112      */
113     public function get_s()
114     {
115         return $this->s;
116     }
117
118     /**
119      * 获取最优值
120      */
121     public function get_bestw()
122     {
123         return $this->bestw;
124     }
125 }
126
127 function branch_limit_FIFO_search()
128 {
129     $n = 3;
130     $c1 = 50;
131     $c2 = 50;
132     $w = array(0,10,40,40);
133     $bfis = new BranchLimitFIFOSearch($n, $c1, $c2, $w);
134     $s = $bfis->get_s();//所有货箱的重量之后
135
136     if($s<=$c1 || $s<=$c2)
137     {
138         die("need only one ship!");
139     }
140     if($s > $c1+$c2)
141     {
142         die("no solution!");
143     }
144
145     $bfis->max_loading($c1);
146     $bestw = $bfis->get_bestw();
147     if($s-$bestw <= $c2)
148     {
149         echo "The first ship loading " . $bestw . "<br/>";
150         echo "The second ship loading " . ($s - $bestw);
151     } else {
152         echo "no solution!!!";
153     }
154 }
155
156 branch_limit_FIFO_search();

时间: 2024-08-04 19:47:51

有两艘船需要装运的n箱货物,第一艘船的载重量是c1,第二艘船的载重量是c2,wi是货箱i的重量,且w1+w2+……+wn<=c1+c2的相关文章

分支界限法(BFS)

分支界限法类似回溯法,也是在问题的解空间上搜索问题解的算法,其求解目标是找出满足约束条件的一个解(回溯是找出所有的解)或是在满足条件的解中找出最优解. 搜索策略:在扩展结点处,先生成其所有的儿子节点(分支),然后再从当前的活结点表中(根据每一活结点计算出的函数值)选择最有利的结点作为下一个扩展结点. 从活结点表中选择下一扩展结点的不同方式导致不同的分支界限法: 1.队列式(FIFO)分支界限法 2.优先队列式分支界限法 基本思想:以广度优先或以最小耗费(最大效益)优先的方式搜索问题的解空间树.

分支界定法 branch-and-bound 分析与实现)(转载)

1. 介绍分支界定法之前需要了解一下广度优先搜索breadth-First-search(BFS) 1.从图中某个顶点V0出发,并访问此顶点:以层为顺序,一层一层往下遍历 2.从V0出发,访问V0的各个未曾访问的邻接点W1,W2,-,Wk;然后,依次从W1,W2,-,Wk出发访问各自未被访问的邻接点 宽度优先搜索算法(又称广度优先搜索)是最简便的图的搜索算法之一,这一算法也是很多重要的图的算法的原型.Dijkstra单源最短路径算法和Prim最小生成树算法都采用了和宽度优先搜索类似的思想.其别名

51Nod - 1596 搬货物

51Nod - 1596 搬货物 现在有n个货物,第i个货物的重量是 2wi .每次搬的时候要求货物重量的总和是一个2的幂.问最少要搬几次能把所有的货物搬完. 样例解释: 1,1,2作为一组. 3,3作为一组. Input 单组测试数据. 第一行有一个整数n (1≤n≤10^6),表示有几个货物. 第二行有n个整数 w1,w2,...,wn,(0≤wi≤10^6). Output 输出最少的运货次数. Input示例 样例输入1 5 1 1 2 3 3 Output示例 样例输出1 2 题解:

1596 搬货物

现在有n个货物,第i个货物的重量是 2wi .每次搬的时候要求货物重量的总和是一个2的幂.问最少要搬几次能把所有的货物搬完. 样例解释: 1,1,2作为一组. 3,3作为一组. Input 单组测试数据. 第一行有一个整数n (1≤n≤10^6),表示有几个货物. 第二行有n个整数 w1,w2,...,wn,(0≤wi≤10^6). Output 输出最少的运货次数. Input示例 样例输入1 5 1 1 2 3 3 Output示例 样例输出1 2 进行二进制进位运算,当有一位为1时sum+

[ZJOI2010]基站选址

题目描述 有N个村庄坐落在一条直线上,第i(i>1)个村庄距离第1个村庄的距离为Di.需要在这些村庄中建立不超过K个通讯基站,在第i个村庄建立基站的费用为Ci.如果在距离第i个村庄不超过Si的范围内建立了一个通讯基站,那么就村庄被基站覆盖了.如果第i个村庄没有被覆盖,则需要向他们补偿,费用为Wi.现在的问题是,选择基站的位置,使得总费用最小. 输入输出格式 输入格式: 输入文件的第一行包含两个整数N,K,含义如上所述. 第二行包含N-1个整数,分别表示D2,D3,-,DN ,这N-1个数是递增的

9-15考试题目程序作业

然而有素质的我选择把题目也粘在上面,喜欢的同学可以做一下 支付宝(pay.pas/c/cpp)题目描述做完前七次Nescafé 模拟赛之后,你已经欠了一大笔电费.现在收电费的已经把你堵在了家里.当然了,你并不是打算一直拖欠电费的人,你只是如果不能用最少的纸币张数凑出电费金额的话会感到十分不爽.本来这是一件很简单的事,但是你平常很少使用标准的纸币,而是习惯去银行领一本支票簿,在每一张上填上自己喜欢的金额然后把这些东西用作纸币付账,这就使问题变得复杂了起来.现在你知道你一共填写了N种金额的支票,第i

P2605 [ZJOI2010]基站选址

题目描述 有N个村庄坐落在一条直线上,第i(i>1)个村庄距离第1个村庄的距离为Di.需要在这些村庄中建立不超过K个通讯基站,在第i个村庄建立基站的费用为Ci.如果在距离第i个村庄不超过Si的范围内建立了一个通讯基站,那么就村庄被基站覆盖了.如果第i个村庄没有被覆盖,则需要向他们补偿,费用为Wi.现在的问题是,选择基站的位置,使得总费用最小. 输入输出格式 输入格式: 输入文件的第一行包含两个整数N,K,含义如上所述. 第二行包含N-1个整数,分别表示D2,D3,…,DN ,这N-1个数是递增的

python机器学习实战(三)

python机器学习实战(三) 版权声明:本文为博主原创文章,转载请指明转载地址 www.cnblogs.com/fydeblog/p/7277205.html  前言 这篇博客是关于机器学习中基于概率论的分类方法--朴素贝叶斯,内容包括朴素贝叶斯分类器,垃圾邮件的分类,解析RSS源数据以及用朴素贝叶斯来分析不同地区的态度. 操作系统:ubuntu14.04 运行环境:anaconda-python2.7-jupyter notebook 参考书籍:机器学习实战和源码,机器学习(周志华) not

【BZOJ1835】[ZJOI2010]base 基站选址 线段树+DP

[BZOJ1835][ZJOI2010]base 基站选址 Description 有N个村庄坐落在一条直线上,第i(i>1)个村庄距离第1个村庄的距离为Di.需要在这些村庄中建立不超过K个通讯基站,在第i个村庄建立基站的费用为Ci.如果在距离第i个村庄不超过Si的范围内建立了一个通讯基站,那么就成它被覆盖了.如果第i个村庄没有被覆盖,则需要向他们补偿,费用为Wi.现在的问题是,选择基站的位置,使得总费用最小. 输入数据 (base.in) 输入文件的第一行包含两个整数N,K,含义如上所述. 第