苹果问题

2 篇文章 0 订阅


原问题:

老板有1000个苹果,却只有10个箱子,客户给老板除了一个难题,说如果老板把题解开就把1000个苹果全买了,老板说没问题,来吧!我之前是程序员。客户说:“把你现在的1000个苹果全部装进10个箱子(分别为1号、2号.....10号),忽略箱子的容量大小,每个箱子都要装苹果,装几个你自己定,我要买几个苹果,你就告诉我,把几号,几号拿走,就是你要的个数。”,老板说:“呵呵。”


问题答案:


此问题是否有解?如何求解?

1.首先我们必须写一个验证的程序

 
    /**
    * 验证数组
    * @author   syh
    * @param    array $arr 
    * @return   bool
    */
    function check($arr)
    {
        rsort($arr);//逆序
        $listNum=range(1,array_sum($arr));
        $fun=function ($num) use ($arr){

            foreach ($arr as $key => $val)
            {
                if($num===$val) return true;
                //减去数组中比自己小的最大值 
                if($num>$val) $num=$num-$val;
            }

            return false;  
        };
        return !in_array(false,array_map($fun,$listNum));

    }
$arr=[1,2,4,8,16,32,64,128,256,489];//此数组为正解的一种,可以通过验证
$data=check($arr);
var_dump($data);//true
2.遍历所有可能的组合或随机取值

2.0非递归遍历(最原始的实现)

 /**
    * $m个苹果分为4堆
    * @author   syh 
    * @param    int $m苹果总数
    * @return   array 验证通过后的数组
    */
    function test($m)
    {
        $listAll=[];
        $list=[];
        $len=4;//苹果堆数
        $max=function($list) use ($m,$len){
           return  $m-array_sum($list)-($len-count($list))+1;

        };

        for ($k1=1,$max1=$max($list);$k1 <=$max1; $k1++)
        { 
            $list[1]=$k1;
            for ($k2=1,$max2=$max($list);$k2 <=$max2 ;$k2++)
            { 
                $list[2]=$k2;
                for ($k3=1,$max3=$max($list);$k3 <=$max3; $k3++)
                {
                    $list[3]=$k3;
                    $list[4]=$m-array_sum($list);
                    if(check($list)) 
                    {
                        $listAll[]=$list;
                    }
                    unset($list[3]);
                    unset($list[4]);
                }
                unset($list[2]);
            } 
            unset($list[1]); 

        }
        return $listAll;

    }

2.1第一种遍历所有可能,

/**
* 递归实现1
* @author   int $m 苹果的个数
* @param    int $n 苹果堆数
* @return   array
*/
function crateArr($m,$n)
{
    static $list=[];//保存当前的组合
    static $listAll=[];//保存所有可能的组合
    static $count=0,$len;
    if(($count++)===0)  $len=$n;//保存数组长度
    $max=$m-array_sum($list)-($len-count($list))+1;

    for ($i=1;$i<=$max;$i++)
    { 

        $list[$n]=$i;//第$n堆苹果     
        if ($n===2)
        {
            //此处为最后一次循环
            $list[$n-1]=$m-array_sum($list);//最后一堆苹果的个数
            if (check($list))
            {
                $listAll[]=$list;  //验证通过后合并        
            }
            unset($list[$n-1]);
            unset($list[$n]);
        }
        elseif($n>2)
        {
            crateArr($m,$n-1);//递归进行 相当于for循环嵌套
            unset($list[$n]);
        }

    }

    return $listAll;//返回所有符合要求的组合
 } 

2.2第一种遍历的次数太多,进一步优化

/**
 *递归实现2
 * @author   syh
 * @param    string $m 苹果总数
 * @param    string $n 苹果堆数
 * @param    string $p 第一堆苹果起始值
 * @return   array
 */
function createArr($m,$n,$p=1)
{
    static $list=[],$listAll=[];
    static $count=0,$len;
    if(($count++)===0)  $len=$n;//保存数组长度
    $d=$len-($len-$n)-1;//第$n项到$len-1项的元素个数
    // 假设第$n堆苹果到$len-1堆苹果数位公差为1的等差数列
    //最后一堆苹果个数=苹果总和-当前堆前面的总和-当前堆到第$len-1堆等差数列的和
    //第$len-1堆苹果的个数为a1+(n-1)*d 等差数列第n项公式
    //当前堆到$len-1的总和为a1*n+n*(n-1)*d/2等差数列前n项和
    // 数组为从小到大排序,当最后一项与倒数第二项相等时为每堆苹果的最大值,即临界点
    $max=($m-array_sum($list)-$d*($d-1)/2-$len+1)/($d+1)+1;

    for ($i=$p;$i<=ceil($max);$i++)
    {  
        $list[$n]=$i;     
        if ($n===2)
        {
            $list[$n-1]=$m-array_sum($list);
            if(check($list))
            {
                // 验证通过后存入数组
                $listAll[]=$list;          
            } 
            unset($list[$n-1]);
            unset($list[$n]);
        }
        elseif($n>2)
        {    
            $function=__FUNCTION__; 
            //堆数递减,后面堆苹果最小个数=前面堆苹果个数+1
            $function($m,$n-1,$i+1);
            unset($list[$n]);
        }

    }   
    return $listAll;
 } 

2.3随机取值

 function crateArr($sum,$len)
    { 
      while(1)
      {
            $arr=[];
            for ($j=1; $j<=$len; $j++) 
            { 
                 $max=$sum-array_sum($arr)-($len-count($arr))+1;
                 $arr[$j]=($j==$len)?$sum-array_sum($arr):mt_rand(1,$max);           
            } 
            if(check($arr)) var_dump($arr); 
        }
    }

总结:经过自己不懈的努力,程序终于实现了;但1000个苹果分成10堆计算量太大,自己的电脑最多能计算300个苹果分成10堆。 
1,2,4,8,16,32,64,128,256,489 
通过观察得出,符合此规律的等比数列,能组合的最大数为2的(数列长度)次方-1。通过程序验证63(2^6-1)个苹果分成6堆苹果只有一个解,小于63就会存在多解;同理,1000个苹果分成10堆,能组合的最大数为2^10-1(1023);所以此问题存在多解,上面的数列只是其中一种解。 
2.4 进一步研究,求全部解


/**
* 遍历后2位
* 此解并不是最终解
* @author   syh
* @return   array
*/
function checkAll()
{
    $arr=[1,2,4,8,16,32,64,128,256,489];
    $list=[];
    for ($i=256; $i >128 ; $i--)
    { 
        array_pop($arr);
        array_pop($arr);
        array_push($arr, $i,745-$i);
        if (check($arr))
        {
            $list[]=implode('--',$arr);
        }
    }
    return $list;
}

 $data=checkAll($arr);
 echo '<pre>';
 print_r($data);
 Array
(
    [0] => 1--2--4--8--16--32--64--128--256--489
    [1] => 1--2--4--8--16--32--64--128--255--490
    [2] => 1--2--4--8--16--32--64--128--254--491
    [3] => 1--2--4--8--16--32--64--128--253--492
    [4] => 1--2--4--8--16--32--64--128--252--493
    [5] => 1--2--4--8--16--32--64--128--251--494
    [6] => 1--2--4--8--16--32--64--128--250--495
    [7] => 1--2--4--8--16--32--64--128--249--496
    [8] => 1--2--4--8--16--32--64--128--248--497
    [9] => 1--2--4--8--16--32--64--128--247--498
    [10] => 1--2--4--8--16--32--64--128--246--499
    [11] => 1--2--4--8--16--32--64--128--245--500
)

转自梦里不知身是客


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值