1.斐波那契数列
斐波那契数列大家都很熟悉,而且知道用递归可以很容易的做出来:
n = int(input)
def fblq(n):
if n == 0:
return 0
elif n == 1:
return 1
else:
return fblq(n-1) + fblq(n-2)
如果用动态规划,就是把结果存到一个数组中:
n = int(input())
dp = []
for i in range(n):
if i in [0,1]:
dp.append(1)
else:
dp.append(dp[i-1] + dp[i-2])
print(dp[-1])
与之类似的还有:跳台阶问题:每次只能跳一个或者两个台阶,跳到n层台阶上有几种方法
填充长方体问题:将一个2*1的长方体填充到2*n的长方体中,有多少种方法
2.数组最大不连续递增子序列
arr = [3,1,4,1,5,9,2,6,5]的最长递增子序列长度为4。
设置一个数组dp,长度为原数组长度,数组第i个位置上的数字代表0...i上最长递增子序列,当增加一个数字时,寻找前面全部比它小的且连续性最多的个数再加1,比如数组当中的5,它比前面的数都大,但最长子序列是345:
n = int(input())
m=list(map(int,input().split()))
dp = [1]*n
for i in range(1,n):
for j in range(i):
if m[i] > m[j] and dp[j] + 1>dp[i]:
dp[i] = dp[j]+1
print(max(dp))
当5进行比较时,虽然他比1大,但是1的dp值+1却没有4的大,因为4的dp值比前面的3大。
3.数组最大连续子序列和
如arr = {6,-1,3,-4,-6,9,2,-2,5}的最大连续子序列和为14。即为:9,2,-2,5
创建一个数组dp,长度为原数组长度,不同位置数字dp[i]代表0...i上最大连续子序列和,初始值为数组中的第一个数字。当进来一个新的数字arr[i+1]时,判断到他前面数字子序列和dp[i]+arr[i+1]跟arr[i+1]哪个大,前者大就保留前者,后者大就说明前面连续数字加起来都不如后者一个新进来的数字大,前面数字就可以舍弃,从arr[i+1]开始,每次比较完都跟max比较一下,最后的max就是最大值。
n = int(input())
m=list(map(int,input().split()))
dp = []
dp[0] = m[0]
for i in range(1,n):
dp.append(max(dp[i-1]+m[i],m[i]))
print(max(dp))
4.数字塔从上到下所有路径中和最大的路径
数字塔是第i行有i个数字组成,从上往下每个数字只能走到他正下方数字或者正右方数字,求数字塔从上到下所有路径中和最大的路径:
n = int(input())
m = []
for i in range(n):
m.append(list(map(int,input().split())))
dp = m[0]
for i in range(1,n):
ls = m[i]
dp1 = [0]*len(ls)
for j in range(len(ls)):
if j == 0:
dp1[j] = dp[j] + ls[j]
elif j == len(ls)-1:
dp1[j] = ls[j] + dp[j-1]
else:
dp1[j] = max(dp[j-1],dp[j])+ls[j]
dp = dp1.copy()
print(max(dp))
'''
3
1 5
8 4 3
2 6 7 9
6 2 3 5 1
最大路径是3-5-3-9-5,和为25。
'''
5.两个字符串最大公共子序列
比如字符串1:BDCABA;字符串2:ABCBDAB,则这两个字符串的最长公共子序列长度为4.
详细描述点这里:点这里
n = input()
m = input()
dp = [[0]*(len(n)+1) for i in range(len(m)+1)]
for i in range(1,(len(m)+1)):
for j in range(1,(len(n)+1)):
if m[i-1] == n[j-1]:
dp[i][j] = dp[i-1][j-1]+1
else:
dp[i][j] = max(dp[i-1][j],dp[i][j-1])
print(dp[-1][-1])
这里注意一下dp二维数组的创建:
dp = [[0]*(len(n)+1)]*(len(m)+1)
#该方法不行,它是相当于复制了[0]*(len(n)+1),当一行改变,全部改变
dp = [[0]*(len(n)+1) for i in range(len(m)+1)]
#该方法可行
6.背包问题
该问题也是创建一个二维dp数组,横坐标为背包可容量,纵坐标为各个物品的编号,dp[i,j]表示当前所拥有的总价值,当第i个物品可以放下时,是不是(i-1,j-wi)时的价值加上该物品的价值,与(i-1,j)的价值相比对,选择大的。如果放不下的话就取(i-1,j)。
n = int(input())#物品数量
wt = int(input())#背包总量
w = list(map(int,input().split()))#各个物品的重量
p = list(map(int,input().split()))#各个物品的价值
dp = [[0]*(wt+1) for i in range(n+1)]
for i in range(1,n+1):
for j in range(1,wt+1):
if j >= w[i-1]:
dp[i][j] = max(dp[i-1][j],dp[i-1][j-w[i-1]] + p[i-1])
#这里不能用dp[i][j-w[i-1]],因为很有可能造成一件物品计算两次甚至多次
else:
dp[i][j] = dp[i-1][j]
print(dp[i])
print(max(dp))
'''
6
10
2 2 3 1 5 2
2 3 1 5 4 3
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2]
[0, 0, 3, 3, 5, 5, 5, 5, 5, 5, 5]
[0, 0, 3, 3, 5, 5, 5, 6, 6, 6, 6]
[0, 5, 5, 8, 8, 10, 10, 10, 11, 11, 11]
[0, 5, 5, 8, 8, 10, 10, 10, 12, 12, 14]
[0, 5, 5, 8, 8, 11, 11, 13, 13, 13, 15]
[0, 5, 5, 8, 8, 11, 11, 13, 13, 13, 15]
'''
可以将其优化成一个滚动数组,只创建一个一维数组,长度为从1到W,初始值都是0,能装得下i时,dp[j] = Math.max(dp[j], dp[j-w[i]]+p[i]);装不下时,dp[j] = dp[j]:
n = int(input())#物品数量
wt = int(input())#背包总量
w = list(map(int,input().split()))#各个物品的重量
p = list(map(int,input().split()))#各个物品的价值
dp = [0]*(wt+1)
for i in range(n):
print(dp)
for j in range(wt,0,-1):
if j >= w[i]:
dp[j] = max(dp[j],dp[j-w[i]]+p[i])
else:
dp[j] = dp[j]
'''
这里需要注意的是第二层循环一定要重后往前,不然从前往后会受前面值得影响
'''
7.找零钱问题:有几种方法
具体思路同背包问题,这里只分析一下动态转化方程,能用这种零钱,分为用了这种零钱的方法跟没用到这种零钱的方法,dp[i][j] = dp[i-1][j] + dp[i][j-num[i]](这里运算法则的原因写出来就能知道了);如果不能用这种零钱,即所组成的面额小于当前零钱,直接等于不用这种零钱的数值,dp[i][j] = dp[i-1][j]。
这里要特别注意的是:1、开始填写二维数组边界值时,第一行是填写只用第一种面额零钱组成相应数额的方法,要注意是总数额除以第一种面额取余为0才能组成,即如果第一种面额为2,不能组成3,5的数额等;2、填写二维数组第一列时,代表到用到面额为i时,剩余数额为0,即只用i就可以组成相应数额,这也是一种方法,比如2,5组成的面额,当target为5时,2是不能构成的,利用上面的算法,恰好能把第一列的1给用上,凑成了一种方法。
target = int(input())#需要换的钱
n = list(map(int,input().split()))#拥有面额
dp = [[0]*(target+1) for i in range(len(n))]
for i in range(target+1):
if i%n[0] == 0:
dp[0][i] = 1
for i in range(len(n)):
dp[i][0] = 1
for i in range(1,len(n)):
for j in range(1,target+1):
if j >= n[i]:
dp[i][j] = dp[i][j-n[i]] + dp[i-1][j]
else:
dp[i][j] = dp[i-1][j]
同背包问题一样,可以用一维数组优化:
target = int(input())#需要换的钱
n = list(map(int,input().split()))#拥有面额
dp = [0]*(target+1)
for i in range(target+1):
if i%n[0] == 0:
dp[i] = 1
dp[0] = 1
for i in range(1,len(n)):
for j in range(1,target+1):
if j >= n[i]:
dp[j] = dp[j-n[i]]+dp[j]
8.找零钱问题:所用面额数量最少
跟上面思路相同,代码不同点:1、填写边界值时,第一行仍是看取余是不是为0,如果为0,填的是除以它的商,即用了几张。2、填写边界值第一列时, 第一列代表用了这一面额的纸币且剩下的数额为0,代表值用着一种纸币就可以构成这种数额,用的张数应该填到(i,j)处,所以第一列都是0;3、写状态转化方程时,要注意判断,如果数额小于面额,直接等于上一层值,dp[i][j]=dp[i-1][j]; 如果数额等于面额,直接等于1;如果数额大于面额,先判断当用掉一张面额时,使用当前面额的剩余数额处是否有值,和不使用当前面额的剩余数额处是否有值,即当dp[i-1][j]!=0&&dp[i][j-num[i]]!=0即这两处都有值,就看那一个更小,如果不都有,仅仅选择一个有值的就好了.
target = int(input())#需要换的钱
n = list(map(int,input().split()))#拥有面额
dp = [[0]*(target+1) for i in range(len(n))]
for i in range(1,target+1):
if i % n[0] == 0:
dp[0][i] = int(i/n[0])
for i in range(len(n)):
dp[i][0] = 0
for i in range(1,len(n)):
for j in range(1,target+1):
if j < n[i]:
dp[i][j] = dp[i-1][j]
elif j == n[i]:
dp[i][j] = 1
else:
if dp[i-1][j] != 0 and dp[i][j-n[i]] != 0:
dp[i][j] = min(dp[i-1][j],dp[i][j-n[i]]+1)
else:
if dp[i-1][j] == 0:
dp[i][j] = dp[i][j-n[i]]+1
else:
dp[i][j] = dp[i-1][j]
滚动数组如下:
target = int(input())#需要换的钱
n = list(map(int,input().split()))#拥有面额
dp = [0]*(target+1)
for i in range(1,target+1):
if i % n[0] == 0:
dp[i] = int(i/n[0])
for i in range(1,len(n)):
for j in range(1,target+1):
if j < n[i]:
dp[j] = dp[j]
elif j == n[i]:
dp[j] = 1
else:
if dp[j] != 0 and dp[j-n[i]] != 0:
dp[j] = min(dp[j],dp[j-n[i]]+1)
else:
if dp[j] == 0:
dp[j] = dp[j-n[i]]+1
else:
dp[j] = dp[j]