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