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

Posted w&y

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了有两艘船需要装运的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();
View Code

 
























以上是关于有两艘船需要装运的n箱货物,第一艘船的载重量是c1,第二艘船的载重量是c2,wi是货箱i的重量,且w1+w2+……+wn<=c1+c2的主要内容,如果未能解决你的问题,请参考以下文章

noj装载问题

noj算法 装载问题 回溯法

hihoCoder 1128 二分查找

2021-12-01:给定一个正数数组arr,代表每个人的体重。给定一个正数limit代表船的载重,所有船都是同样的载重量。 每个人的体重都一定不大于船的载重。 要求: 1, 可以1个人单独一搜船;(

noip2016海港

洛谷P2058 海港