当前位置:Gxlcms > PHP教程 > php解决装箱问题

php解决装箱问题

时间:2021-07-01 10:21:17 帮助过:20人阅读

我对装箱问题,石头过河问题的解法
见http://www.oschina.net/question/117304_112681

实现思路主要为:
1. 大块石头必须优先装箱(早装和留到后面装都要装,先解决之)
2. 优先装重量接近w的
3. 同样重量优先装多块,如装9,6和装9,5,1比,则优先951装箱
4. 使用php的函数以简化代码,并使用根据k值生成函数的技巧
5. 此类问题由于本身性质,计算量较大,请酌情设置参数测试。

示例输出:(当rocks为1~9,w为15,k为3)
寻找由 3 个元素组成的最大解:
Array
(
[0] => 9
[1] => 5
[2] => 1
)

寻找由 2 个元素组成的最大解:
Array
(
[0] => 9
[1] => 6
)

寻找由 1 个元素组成的最大解:
Array
(
[0] => 9
)

寻找由 3 个元素组成的最大解:
Array
(
[0] => 8
[1] => 4
[2] => 3
)

寻找由 2 个元素组成的最大解:
Array
(
[0] => 8
[1] => 7
)

寻找由 1 个元素组成的最大解:
Array
(
[0] => 8
)

寻找由 3 个元素组成的最大解:
Array
(
[0] => 7
[1] => 6
[2] => 2
)

寻找由 2 个元素组成的最大解:
Array
(
[0] => 7
[1] => 6
)

寻找由 1 个元素组成的最大解:
Array
(
[0] => 7
)

最小次数:3
装船过程:Array
(
[0] => Array
(
[0] => 9
[1] => 5
[2] => 1
)

[1] => Array
(
[0] => 8
[1] => 4
[2] => 3
)

[2] => Array
(
[0] => 7
[1] => 6
[2] => 2
)

)
  1. // php 练习 之装箱问题
  2. // author: mx (http://my.oschina.net/meikaiyuan)
  3. // 2013/5/30
  4. // 问题:
  5. // http://www.oschina.net/question/117304_112681
  6. /*
  7. 题目:
  8. 以前问过类似问题,没有很好解答。所以想再问一次。
  9. 有大大小小的一堆石头要用船拉到河对岸
  10. --石头有m块,每块的重量已知
  11. --船每次只能装k块石头,并且装载重量不可以超过w
  12. --想求出最少几趟能把全部石头运过河。
  13. ------------------------------------
  14. 例1
  15. 石头有9块,重量分别是1,2,3,4,5,6,7,8,9
  16. k=3
  17. w=15
  18. 那么结果是,最少3次就可以运完。
  19. ------------------------------------
  20. 例2
  21. 石头有9块,重量分别是1,1,1,5,6,6,7,9,9
  22. k=3
  23. w=15
  24. 那么结果是,最少 4 次才可以运完。
  25. */
  26. //代码开始
  27. //石头
  28. global $rocks;
  29. // 船每次最多装几块
  30. global $k;
  31. // 船最大载重量
  32. global $w;
  33. $k=3;
  34. $rocks=array(1,2,3,4,5,6,7,8,9);
  35. // $rocks=array(1,1,1,5,6,6,7,9,9); //换成这组数据试试结果?
  36. $w=15;
  37. // 当前运了几次
  38. $count=0;
  39. // 运输过程,二维数组,形如 1=>array(9,5,1),表示第几次运了哪一些
  40. $process=array();
  41. // 求数组$rocks中一组合,使得最多$k个元素且这些元素的和尽可能大但小于等于指定值$w, 数组已经按从大到小排序过
  42. function getMaxCombination( ) {
  43. //石头
  44. global $rocks;
  45. // 船每次最多装几块
  46. global $k;
  47. // 船最大载重量
  48. global $w;
  49. // 保存各种$k下满足所有元素之和小于等于w且最大的集合
  50. $k_w_result=array();
  51. // 最大组合值
  52. $max_sum=0;
  53. // 哪项最大
  54. $max_one=0;
  55. for ($start=$k;$start>0;$start--){
  56. // 找到由固定$start个元素组成的最大解
  57. $start_w_arr = getMaxCombination2($start);
  58. echo "寻找由 $start 个元素组成的最大解: \n";
  59. print_r($start_w_arr);
  60. echo "\n";
  61. $sum=array_sum( $start_w_arr );
  62. // 注意:因为是降序排列的,$k--, 越早找到的同sum的组合$k越大,也就是解越好,所以是小于不是小于等于
  63. if($sum>$max_sum){
  64. $max_sum=$sum;
  65. $max_one=$k-$start;
  66. }
  67. $k_w_result[]= $start_w_arr ;
  68. }
  69. return $k_w_result[$max_one];
  70. }
  71. // 求数组$rocks中一由给定$start个元素构成的组合,这些元素的和尽可能大但小于等于指定值$w, 数组已经按从大到小排序过
  72. function getMaxCombination2($start ) {
  73. //石头们
  74. global $rocks;
  75. // 船每次最多装几块
  76. global $k;
  77. // 船最大载重量
  78. global $w;
  79. if(count($rocks)<$start){
  80. return array(0);
  81. }
  82. $c=count($rocks);
  83. // 根据$start生成一函数,内含$start层for循环代码, 然后包含进来再调用此函数
  84. if(!file_exists( "$start.php")){
  85. $output_1="";
  86. $output_2='$sum=';
  87. $output_3='if($sum<=$w){ $arr=array(); ';
  88. $output_4='';
  89. for($i=0;$i<$start;$i++){
  90. $output_1.='for($p'.$i.'='.$i.';$p'.$i.'<$c-'.($start-$i-1).';$p'.$i.'++){'."\n";
  91. if($i>0){
  92. $output_2.='+';
  93. }
  94. $output_2.='$rocks[$p'.$i.']';
  95. $output_3.='$arr[]=$rocks[$p'.$i.'];';
  96. $output_4.='}';
  97. }
  98. $output_2.=';';
  99. $output_3.=' return $arr; }' ;
  100. $output='';
  101. file_put_contents("$start.php",$output);
  102. include_once "$start.php";
  103. }
  104. else{
  105. include_once "$start.php";
  106. }
  107. return call_user_func('myfor'.$start ,$rocks,$c,$w);
  108. }
  109. //开始计算
  110. // 数组先从大到小排序, 此操作是后续算法省时省力的关键
  111. rsort($rocks);
  112. // 为了防止石头过大船过小造成下面算法死循环
  113. foreach ($rocks as $v){
  114. if($v>$w){
  115. die("有石头不可能装船,换大船来再战!");
  116. }
  117. }
  118. // 算法开始
  119. while(!empty($rocks)){
  120. // 开始装一船
  121. $process[$count]=array();
  122. // 装船
  123. $process[$count]= getMaxCombination( ) ;
  124. // 从石头中移除已经装船的
  125. foreach($process[$count] as $v){
  126. $key=array_search($v, $rocks);
  127. unset( $rocks[$key]);
  128. }
  129. $rocks=array_values($rocks);
  130. // 装船数+1
  131. $count++;
  132. }
  133. // 输出结果
  134. echo '最小次数:'.$count."\n";
  135. echo '装船过程:';
  136. print_r($process);
  137. ?>

人气教程排行