1. 程式人生 > >USTCOJ 1378/POJ 1664 放蘋果 解法

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) 。

相關分析及程式碼,可參考:

另,簡潔、且記錄函式呼叫返回值的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)。

相關資料可參考:

解法三:

列舉所有可能的分拆。

將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)