USTCOJ 1378/POJ 1664 放苹果 解法

百炼1664,放苹果:http://poj.grids.cn/practice/1664

分枚举和计数两类解法。计数更为快捷。解法一和解法二分别是两类不同的计数方法。解法三是枚举法。


解法一:

设f(m,n) 为m个苹果,n个盘子的放法数目则有:

①当n>m:必定有n-m个盘子永远空着,去掉它们对摆放苹果方法数目不产生影响。即if(n>m) f(m,n) = f(m,m)。

②当n<=m:不同的放法可以分成两类:
    a、有至少一个盘子空着,即相当于f(m,n) = f(m,n-1);
    b、所有盘子都有苹果,相当于可以从每个盘子中拿掉一个苹果,不影响不同放法的数目,即f(m,n) = f(m-n,n).
而总的放苹果的放法数目等于两者的和,即 f(m,n) =f(m,n-1)+f(m-n,n) 。

相关分析及代码,可参考:

简洁版:http://www.cnblogs.com/dongsheng/archive/2012/08/15/2640468.html

值记录版:http://blog.csdn.net/jackyzhengx1990/article/details/6049218

另,简洁、且记录函数调用返回值的Python代码如下。

#版本:python v3.3.0
import sys
from functools import lru_cache

#根据参数建表,保存已调用过的函数的返回值
@lru_cache(maxsize=None)        
def counter(m, n):
    if m == 0 or n == 1:
        return 1
    if n > m:
        return counter(m, m)
    else:
        return counter(m, n-1)+counter(m-n, n)


#重定向输入输出
sys.stdin = open("1378.in", "r")
sys.stdout = open("1378.out", "w")

line = input()
n = int(line)
for i in range(n):
    #读入m、n的值。
    line = input()
    m, n = [int(num) for num in line.split()]

    #计算并输出结果    
    print(counter(m ,n))
注:其中通过装饰器函数lru_cache自动记录调用过的counter函数的返回值。这样,下次以相同的参数调用counter函数时,将直接返回之前计算所得值。


解法二:

将这个问题转化为正整数的有序分拆。我们用B(m, n)来表示m的n分拆的个数。所谓m的n分拆,是指将正整数m拆分为n个正整数之和。例如:B(3, 2)=1,仅“3 = 1 + 2”一种拆法。B(4, 2)=2,有“4=1+3、4=2+2”两种拆法。

那么,将m个相同的苹果放入n个相同的盘子中,也就相当于将m拆分为n个非负整数之和。记A(m, n)为将m个苹果放入n个盘子中的放法。应有A(m, n)=B(m, 1)+B(m, 2)+B(m, 3)+.....B(m, n)。

又B(m+k, k)=B(m, 1)+B(m, 2)+B(m, 3)+B(m, 4)+....B(m, k),且有B(m, 1)=1, B(m, m)=1。于是求解A(m, n)转化为递归求解B(m+n, n)。

相关资料可参考:

其中B(m, n)递归式来源(定理2.6.3):http://wenku.baidu.com/view/3a9c541c0b4e767f5acfced9.html

整数无序分拆:http://en.wikipedia.org/wiki/Partition_(number_theory)

整数有序分拆:http://en.wikipedia.org/wiki/Composition_(number_theory)


解法三:

枚举所有可能的分拆。

将m个相同的苹果放入n个相同的盘子可能的放法等价于求出下列方程解的个数:

m = a1+a2+a3+....+an,其中0≤a1≤a2≤a3≤....≤an≤m

可通过枚举上述式子解的个数求解。代码如下:

#版本:python v3.3.0
import sys
   
def place(apple, bowl, last):    
    global counter      #声明全局变量
    if bowl == 1:
        if apple >= last:
            counter += 1
        return

    if apple < last:
        return

    for i in range(last, apple+1):
        place(apple-i, bowl-1, i)


#重定向输入输出
sys.stdin = open("1378.in", "r")
sys.stdout = open("1378.out", "w")

line = input()
n = int(line)
for i in range(n):
    #读入m、n的值。
    line = input()
    m, n = [int(num) for num in line.split()]

    #初始化counter,计算并输出结果
    counter = 0
    place(m ,n , 0)
    print(counter)


你可能感兴趣的:(组合数学,放苹果)