整数划分问题 递归 动态规划 openjudge 百练 python

4117:简单的整数划分问题
http://bailian.openjudge.cn/practice/4117
4119:复杂的整数划分问题
http://bailian.openjudge.cn/practice/4119

# 把正整数N划分成 K个正整数 之和的划分数目 0 < K <= N
def NK_dp(n, k):
    dp = [[0 for i in range(n + 1)] for j in range(n + 1)]
    dp[0][0] = 1  # 第1行 = 1
    for i in range(1, n + 1):  # 遍历第[1,n]行
        for j in range(0, i + 1):  # 遍历第[0,i]列
            if j == 1:  # 第1行 = 1
                dp[i][j] = 1
            else:  # 左上 + 上
                dp[i][j] = dp[i - 1][j - 1] + dp[i - j][j]
    return dp[n][k]


# 把正整数N划分成 K个正整数 之和的划分数目 0 < K <= N
def NK_recur(n, k):
    # NK_recur(0, 0) = 1 为什么??????????
    if n == k or k == 1:
        # 当n == k时,N划分成 K个1
        # 当k == 1时,N划分成 1个N
        return 1  # 对角线和第1行 = 1
    elif n < 1 or k < 1 or n < k:
        return 0  # 第0行,第0列,对角线右上方 = 0
    else:  # 左上 + 上
        # 把正整数i划分成 j个正整数(不存在1) 之和的划分数目 =
        # 把正整数(i-j)划分成 j个正整数 之和的划分数目 NK_recur[i-j][j] 每个整数都同时减1
        # 把正整数i划分成 j个正整数(存在1) 之和的划分数目 =
        # 把正整数(i-1)划分成 (j-1)个正整数 之和的划分数目 NK_recur[i-1][j-1] 去掉里面的1
        return NK_recur(n - 1, k - 1) + NK_recur(n - k, k)


# 把正整数n划分成 n<m的 若干个 不同正整数 之和的划分数目
# N划分成 若干个不同正整数 之和的划分数目
def Ndiff_dp(n):
    dp = [[0 for i in range(n + 1)] for j in range(n + 1)]
    dp[0][0] = 1  # 除了[0][0]是1之外,第0列都是0
    for i in range(n + 1):  # 遍历第[0,n]行
        for j in range(1, n + 1):  # 遍历第[1,n]列
            if i < j:
                dp[i][j] = dp[i][i]
            else:
                dp[i][j] = dp[i][j - 1] + dp[i - j][j - 1]
    return dp[n][n]


# 把正整数n划分成 n<m的 若干个 不同正整数 之和的划分数目
def Ndiff_recur(n, m):
    if (n == 0):  #
        return 1  # 第0行 为什么是1???
    elif (m < 1):  # 因为 m < 1 不是正整数,所以返回0
        return 0  # 除了[0][0]是1之外,第0列都是0
    elif (n < m):
        return Ndiff_recur(n, n)  # 左
    elif (n == m):
        # 当划分包含 m 时, m = m ,只有一种划分方法;
        # 当 不包含 m 时, 有 func(n, m - 1) 种划分方法。
        return Ndiff_recur(n, m - 1) + 1  # 左 + 1
    elif (n > m):  # 上左 + 左
        # (1)划分中包含 m 时,有func(n - m, m - 1)种划分方法
        # (2)划分中不包含 m 时,有func(n, m - 1)种划分方法
        return Ndiff_recur(n - m, m - 1) + Ndiff_recur(n, m - 1)


# 把正整数n划分成 n<m的 若干个 可相同的正整数 之和的划分数目
def Nsame_recur(n, m):
    if (n == 1 or m == 1):
        # (1)当 n == 1 时,无论 m 取何值,都只能 划分为 1;
        # (2)当 m == 1 时 ,无论 n 为何值, 也只能划分为 n 个 1 相加 ,只有一种划分方法;
        return 1
    elif (n < m):
        return Nsame_recur(n, n)  # 左
    elif (n == m):
        # 当划分包含 m 时, m = m ,只有一种划分方法;
        # 当 不包含 m 时, 有 func(n, m-1) 种划分方法。
        return Nsame_recur(n, m - 1) + 1  # 左 + 1
    elif (n > m):
        # (1)划分中包含 m 时, 有func(n -m, m) 种划分方法;
        # (2)划分中不包含 m 时, 有 func(n, m-1)种划分方法;
        return Nsame_recur(n - m, m) + Nsame_recur(n, m - 1)  # 上 + 左


# 把整数 n 划分为 <= n 的一系列数字之和 的划分方法种数
def Nsame_dp(n):
    f = [[0 for t in range(n + 1)] for i in range(n + 1)]
    for i in range(1, n + 1):  # 从左到右遍历列[1,n]
        f[i][1] = 1  # 第1列 = 1
    for m in range(2, n + 1):  # 从左到右遍历列[2,n]
        f[1][m] = 1  # 第1行 = 1
        for i in range(2, n + 1):  # 从上到下遍历行[2,n]
            if (i < m):  # n < m 在对角线上方
                f[i][m] = f[i][i]
            elif (i == m):
                f[i][m] = f[i][m - 1] + 1
            else:  # n >= m 在对角线上或其下方
                f[i][m] = f[i - m][m] + f[i][m - 1]
    return f[n][n]


def DivideInt_dp(N, K):  # 动态规划
    dp1 = [[0 for i in range(N + 1)] for j in range(N + 1)]
    dp2 = [[0 for i in range(N + 1)] for j in range(N + 1)]
    dp3 = [[0 for i in range(N + 1)] for j in range(N + 1)]
    # dp[n][k] 把正整数n划分成 k个正整数 之和的划分数目 0 < K <= N
    # dp[n][m] 把正整数n划分成 n<m的 若干个 不同正整数 之和的划分数目
    # dp[n][m] 把正整数n划分成 n<m的 若干个 可相同的正整数 之和的划分数目
    for i in range(1, N + 1):
        for j in range(1, N + 1):
            if (i < j):
                dp1[i][j] = 0
                dp2[i][j] = dp2[i][i]
                dp3[i][j] = dp3[i][i]
            elif (i == j):
                dp1[i][j] = 1
                dp2[i][j] = dp2[i][j - 1] + 1
                dp3[i][j] = dp3[i][j - 1] + 1
            else:
                # dp[n][k] = dp[n - k][k] + dp[n - 1][k - 1]
                dp1[i][j] = dp1[i - j][j] + dp1[i - 1][j - 1]
                # dp[n][m] = dp[n][m - 1] + dp[n - m][m - 1]
                dp2[i][j] = dp2[i][j - 1] + dp2[i - j][j - 1]
                # dp[n][m] = dp[n][m - 1] + dp[n - m][m]
                dp3[i][j] = dp3[i][j - 1] + dp3[i - j][j]
    # dp1[N][K] 把正整数N划分成 K个正整数 之和的划分数目 0 < K <= N
    # dp2[N][N] 把正整数N划分成 若干个 不同正整数 之和的划分数目
    # dp3[N][N] 把正整数N划分成 若干个 可相同的正整数 之和的划分数目
    return dp1[N][K], dp2[N][N], dp3[N][N]


def DivideOddInt_dp(N):
    f = [[0 for i in range(N + 1)] for j in range(N + 1)]  # N划分成K个奇正整数之和的划分数目
    g = [[0 for i in range(N + 1)] for j in range(N + 1)]  # N划分成K个偶正整数之和的划分数目
    # 把i-j划分成j个正奇数 → 把j个正奇数分别+1 → 把i划分成j个正偶数,即 f[i-j][j] = g[i][j]
    # 把i-j划分成j个正偶数 → 把j个正偶数分别+1 → 把i划分成j个正奇数,即 g[i-j][j] = f[i][j]
    f[0][0] = 1
    g[0][0] = 1
    for i in range(1, N + 1):  # 遍历第[1,N]行
        for j in range(1, i + 1):  # 遍历第[1,i]列
            g[i][j] = f[i - j][j]  # 上
            # 划分中不包含1:划分中每个数都>1,那么每个数都-1变成j个正偶数
            # 问题变为 把i-j划分成j个正偶数 g[i-j][j]
            # 划分中包含1:把这个1去掉,问题变为 把i-1划分成j-1个正奇数 f[i-1][j-1]
            f[i][j] = f[i - 1][j - 1] + g[i - j][j]  # 上左 + 上
    return f, g


# 把正整数N划分成 若干个 奇正整数 之和的划分数目
def Nodd_dp(n):
    dp = [[0 for i in range(n + 1)] for j in range(n + 1)]
    for i in range(0, n + 1):
        dp[i][1] = 1
        if (i % 2) == 1:
            dp[0][i] = 1  # 预处理第0层
    for i in range(1, n + 1):
        for j in range(1, n + 1):
            if (j % 2) == 1:
                if (j <= i):
                    dp[i][j] = dp[i - j][j] + dp[i][j - 1]
                else:
                    dp[i][j] = dp[i][i]
            else:
                dp[i][j] = dp[i][j - 1]  # 当前非奇数
    return dp[n][n]


# https://blog.csdn.net/tp7309/article/details/54880495
while True:
    try:
        line = input().split()
    except EOFError:
        break
    N = int(line[0])
    K = int(line[1])

    # dp1[N][K] 把正整数N划分成 K个正整数 之和的划分数目 0 < K <= N
    # dp2[N][N] 把正整数N划分成 若干个 不同正整数 之和的划分数目
    # dp3[N][N] 把正整数N划分成 若干个 可相同的正整数 之和的划分数目
    ans1, ans2, ans3 = DivideInt_dp(N, K)

    # 把正整数N划分成K个奇正整数之和的划分数目
    # 把正整数N划分成K个偶正整数之和的划分数目
    ff, gg = DivideOddInt_dp(N)

    # dp1[N][K] 把正整数N划分成 K个正整数 之和的划分数目 0 < K <= N
    print(ans1)
    print(NK_recur(N, K))
    print(NK_dp(N, K))
    print()
    # dp2[N][N] 把正整数N划分成 若干个 不同正整数 之和的划分数目
    print(ans2)
    print(Ndiff_recur(N, N))
    print(Ndiff_dp(N))
    print()
    # dp3[N][N] 把正整数N划分成 若干个 可相同的正整数 之和的划分数目
    print(ans3)
    print(Nsame_recur(N, N))
    print(Nsame_dp(N))
    print()
    # 把正整数N划分成 若干个 奇正整数 之和的划分数目
    sumsum = 0  # 对第N行求和
    for i in range(0, N + 1):
        sumsum += ff[N][i]
    print(sumsum)
    print(Nodd_dp(N))
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

zzz的学习笔记本

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值