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程式碼如下。
注:其中通過裝飾器函式lru_cache自動記錄呼叫過的counter函式的返回值。這樣,下次以相同的引數呼叫counter函式時,將直接返回之前計算所得值。#版本: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))
解法二:
將這個問題轉化為正整數的有序分拆。我們用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)