Skip to content

M01664: 放苹果

dp, dfs, http://cs101.openjudge.cn/practice/01664/

把M个同样的苹果放在N个同样的盘子里,允许有的盘子空着不放,问共有多少种不同的分法?(用K表示)5,1,1和1,5,1 是同一种分法。

输入

第一行是测试数据的数目t(0 <= t <= 20)。以下每行均包含二个整数M和N,以空格分开。1<=M,N<=10。

输出

对输入的每组数据M和N,用一行输出相应的K。

样例输入

1
7 3

样例输出

8

来源

lwx@POJ

将问题转化为动态规划(DP)形式,可以通过构造一个二维DP数组来解决问题,其中 dp[m][n] 表示将m 个苹果放入 n 个盘子的分法数。

状态转移方程

  1. 如果没有苹果或者只有一个盘子:
    • dp[m][n]=1(基础情况)。
  2. 如果盘子数大于苹果数:
    • dp[m][n]=dp[m][m](因为多余的盘子没有意义)。
  3. 一般情况下:
    • dp[m][n]=dp[m][n−1]+dp[m−n][n]
    • 其中:
      • dp[m][n−1] 表示至少有一个盘子空着的情况。
      • dp[m−n][n] 表示每个盘子至少放一个苹果的情况。
python
def apple_distribution(t, cases):
    # 最大苹果数和盘子数
    max_m = max(c[0] for c in cases)
    max_n = max(c[1] for c in cases)
    
    # 初始化DP数组
    dp = [[0] * (max_n + 1) for _ in range(max_m + 1)]
    
    # 基础情况
    for m in range(max_m + 1):
        dp[m][1] = 1  # 只有一个盘子
    for n in range(max_n + 1):
        dp[0][n] = 1  # 没有苹果
    
    # 填表
    for m in range(1, max_m + 1):
        for n in range(2, max_n + 1):
            if n > m:
                dp[m][n] = dp[m][m]  # 盘子多于苹果
            else:
                dp[m][n] = dp[m][n-1] + dp[m-n][n]
    
    # 处理每个测试用例
    results = []
    for m, n in cases:
        results.append(dp[m][n])
    
    return results

# 主函数
def main():
    t = int(input())  # 测试数据数目
    cases = []
    for _ in range(t):
        m, n = map(int, input().split())
        cases.append((m, n))
    results = apple_distribution(t, cases)
    for res in results:
        print(res)

# 样例测试
if __name__ == "__main__":
    main()

时间复杂度

  • 预处理 DP 表的复杂度为 O(M×N),其中 M,N 是最大苹果数和盘子数。

dfs

python
def count_ways(m, n):
    # 如果只有一个盘子,只能有一种分法
    if n == 1:
        return 1
    # 如果苹果数为0,只有一种分法:所有盘子空
    if m == 0:
        return 1
    # 如果盘子数大于苹果数,最多只需用前m个盘子分苹果
    if n > m:
        return count_ways(m, m)
    # 分为两种情况:至少每个盘子放一个苹果;有盘子空着
    return count_ways(m, n - 1) + count_ways(m - n, n)


t = int(input())  # 测试数据数目
results = []
for _ in range(t):
    m, n = map(int, input().split())
    results.append(count_ways(m, n))
# 输出结果
for res in results:
    print(res)

【陈子良 25物理学院】常规dfs题,按从左到右的顺序遍历盘子,每次选择若干个苹果放进盘子中。为了去重,要求苹果数是单调递减的。

python
for _ in range(int(input())):
    M,N=map(int,input().split())
    s=0
    # n表示当前盘子位置,m表示剩余苹果数,i表示上一个盘子的苹果数
    def dfs(m,n,i):
        if n==N:
            if m<=i:
                global s
                s+=1
            return
        for x in range(min(m,i)+1):
            dfs(m-x,n+1,x)
    dfs(M,1,float('inf'))
    print(s)