球盒问题之一:方案概览

球盒问题之一:方案概览

一、分类

首先看一下分类,主要有8种:
1)球 同,盒 同,无 空箱
2)球 同,盒 同,允许空箱
3)球 同,盒不同,无 空箱
4) 球 同,盒不同,允许空箱
5) 球不同,盒相同,无 空箱
6)球不同,盒相同,允许空箱
7) 球不同,盒不同,无 空箱
8)球不同,盒不同,允许空箱

做这种题型关键是要对号入座,下面的解释分析统一假设m个球,n个盒子。
先从最简单入手,第8种,每个球都有n种选择,所以是
剩下的我们先从前四种(数字都不会太大,且分析较简单)开始。
做题时一看到球同,盒同,就想到凑数法,事实证明这是最快的一种方法。

情况1

如第(1)种,假设m=7,n=4.它的情况只有 1 1 1 4
1 1 2 3
1 2 2 2
这3种情况,所以答案是3.

结论 n个相同的球放入m个相同的盒子(n≥m),不能有空盒时的放法种数等于n分解为m个数的和的种数.

情况2

第(2)种是在第(1)种的基础上延伸 它的情况如下
0,0,0,7
0,0,1,6
0,0,2,5
0,0,3,4
0,1,1,5
0,1,2,4
0,1,3,3
0,2,2,3
1,1,1,4
1,1,2,3
1,2,2,2
所以答案是11种。

结论 n个相同的球放入m个相同的盒子(n≥m),可以有空盒时的放法种数等于将n分解为m个、(m-1)个、(m-2)个、…、2个、1个数的和的所有种数之和.

情况3、4

第(3)种,典型的插板法(不懂的网上搜一下)。记住就行
结论 n个相同的球放入m个不同的盒子中(n≥m),不能有空盒的放法数 Cm1n1 .

第(4)种,是上面方法的延伸,同样记住就行
结论 n个相同的球放入m个不同的盒子中(n≥m),可以有空盒的放法数 Cm1m+n1 .

情况5、6、7

下面分析球不同的 (5) (6) (7)3种情况
先给各位献上一张表,大家别看到数字就害怕了,其实也就是类似与乘法口诀表,(5)(6)(7)的答案都可以在这个表上找到。

这里写图片描述

看一下图上的数字是怎么来的,看下面解释
第一左右两边都是1,第几行就有几个数,比如第5行就是1XXX1
第二 S(n,k)=S(n-1,k-1)+k*S(n-1,k),含义是第N排的第K个数等于他上一排的上一个位置数字加上一排的同样位置数字的K倍 。

例如S(7,3)就是第7排第3个数字,所以他等于上排第6排第2个数字+第6排第3个位置*3
所以画图的话,明显第1排是1,第2排1,1,推理第3排(左右两边都是1,只有中间那个数字没确定)

所以S(3,2)=第2排第1个数字+第2排第2个数字两倍=1+1*2=3,所以第3排数字就是1,3,1.同理S(4,2)=S(3,1)+2*S(3,2)=1+2*3=7,
S(4,3)=S(3,2)+3*S(3,3)=3+3*1=6……如此类推三角形。


此三角形即第(5)种情形的解即:N不同球,M同箱子,无空箱。一共有S(N,M)种分法,比如7个不同球,4个相同箱子,每个箱子至少一个,则看三角形的第7行,第4个数字多少。

结论 n个不同的球放入m个相同的盒子中(n≥m),不能有空盒的放法种数等于n个不同的球分成m堆的种数.

而类型6,N不同球,M同箱,允许空的时候(在类型5的基础上允许空箱)。明显是N个球不变,一个空箱子都没有+有一个空箱子+有两个空箱子+有三个空箱子+,,,,,,都装在一个箱子。说的简单点一共有就是

S(N,1)+S(N,2)+S(N,3)+……….S(N,M)=也就是说第N排开始第1个数字一直加到第M个数字就是总的分法

结论 n个不同的球放入m个相同的盒子中(n≥m),可以有空盒的放法种数等于将n个不同的球分成m堆、(m-1)堆、(m-2)堆、…、2堆、1堆的所有种数之和.

而类型7同样是在类型5的基础上升华,因为5是箱同的,而7箱不同,所以箱子自身多了P(M,M)=M!倍可能
所以类型7的公式就是M!乘以S(N,M)

结论 n个不同的球放入m个不同的盒子中,不能有空盒的放法种数等于n个不同的球分成m堆的种数乘以m!

二、代码

如何用代码实现S(n,m)值的计算。由S(n,k)=S(n-1,k-1)+k*S(n-1,k)这个公式可以知道,这个可以分类到动态规划相关的领域内。

从上文的图片中也可以发现,数值个数是按行以1的步长递增的。如果用矩阵,一个上三角或者下三角矩阵即可存储。而一个三角矩阵也是可以用一个数组来存储的。

实现思路:
用数组存储s(n,k)的值。n行,k列对应第((n+1)n/2+k)个元素(n、k均从0开始)。即第n行(从0开始)前面有n行,i行有i个元素。计算所有元素个数即可得到当前(n,k)元素在数组中的位置。
给出前3个元素的值存入数组中。后续如果要求新的位置的值,根据公式取已有元素的值运算得出新位置的值。
代码如下:

class ComposeTriangle
{
public:
    //赋值前3个元素,最大元素坐标为(1,1)。
    ComposeTriangle():curRow_(1),curCol_(1){triangle_.assign(3,1);};
    virtual ~ComposeTriangle(){};

    std::vector<int> getArr(){return triangle_;};
    int getNum(int row,int col);


private:
    std::vector<int> triangle_;
    //存储最大位置元素的行位置与列位置
    int curRow_;
    int curCol_;

};


int ComposeTriangle::getNum(int row, int col)
{
    while(triangle_.size()<=((row+1)*row/2+col)){
        //计算新的行与列位置。最后一列则行数加1,列数清0。否则列数加1。
        if(curRow_>curCol_){
            curCol_++;
        }else{
            curRow_++;
            curCol_=0;
        }

        //用当前位置的行列索引来计算新值。第0列与最后一列元素值为1。
        if(curCol_==curRow_ || 0 == curCol_){
            triangle_.push_back(1);
        }else{
            //由于行列位置都是从0开始计数,倍乘系数加1。
            triangle_.push_back(getNum(curRow_-1,curCol_-1)+(1+curCol_)*getNum(curRow_-1,curCol_));
        }

    }
    int ret=triangle_.at((row+1)*row/2+col);
    return ret;

}

计算出来的数据如下:

1 
1 1 
1 3 1 
1 7 6 1 
1 15 25 10 1 
1 31 90 65 15 1 
1 63 301 350 140 21 1 
1 127 966 1701 1050 266 28 1 
1 255 3025 7770 6951 2646 462 36 1 
1 511 9330 34105 42525 22827 5880 750 45 1 
1 1023 28501 145750 246730 179487 63987 11880 1155 55 1 
1 2047 86526 611501 1379400 1323652 627396 159027 22275 1705 66 1 

用有符号整型做为元素值类型时,当行数较大时会由于翻转出现负数。如下图16行所示。
这里写图片描述

打印三角形的代码如下:

ComposeTriangle comp;
std::vector<int> vec;
int i=comp.getNumSec(20,2);
vec=comp.getArr();
int j=1;
for(i=0;i<vec.size();i++){
    std::cout<<vec.at(i)<<" ";
    if(i+1==j*(j+1)/2)
    {
        std::cout<<std::endl;
        j++;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值