1.最长公共子序列长度
对于两个字符串,请设计一个高效算法,求他们的最长公共子序列的长度。
思路:用两个指针i和j从后往前遍历s1和s2,如果s1[i]==s2[j],那么这个字符一定在lcs中;否则的话,s1[i]和s2[j]这两个字符至少有一个不在lcs中,需要丢弃一个。
步骤:1.明确dp数组含义 dp[i][j]的含义是:对于s1[1…i]和s2[1…j],它们的 LCS 长度是dp[i][j]。
2.定义base case 让索引为 0 的行和列表示空串,dp[0][…]和dp[…][0]都应该初始化为 0,这就是 base case。
3.找状态转移方程:
str1=input("")
str2=input("")
n=len(str1)
m=len(str2)
dp=[None]*(n+1)
for i in range(n+1):
dp[i]=[0]*(m+1)
dp[0][0]=0
for i in range(1,n+1):
for j in range(1,m+1):
if str1[i-1]==str2[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[n][m])
2.最长公共子序列序列
思路:从dp矩阵中最后位置开始遍历:如果str[i]==str[j]则记录下当前位置的字符,并前i-1,j-1向前走。如果不相等,则比较dp[i-1][j]与dp[i][j-1] 选择序列较大的位置进行向前遍历。
if dp[n][m]==0:
print(-1)
else:
lis=[None]*(dp[n][m])
i=n
j=m
k=dp[n][m]-1
while i>=1 and j>=1:
if str1[i-1]==str2[j-1]:
lis[k]=str1[i-1]
k-=1
i-=1
j-=1
else:
if dp[i][j-1]>=dp[i-1][j]:
j=j-1
elif dp[i][j-1]
3.将整数N分为K份,总共有多少种分法? (类似:n个苹果放入m个盘子有多少种方法)
思路 :因为每份至少要分1,所以先取K份每份分1,剩下数字(N-K)任意分,可以是分在一份,两份,…到K份
所以有 D[N,K]=D[D-K][1]+D[D-K][2]+***+D[D-K][K] ①
D[N-1][K-1]=D[(D-1)-(K-1)][1]+***+D[(D-1)-(K-1)][K-1] ②
由①②两式有:D[N,K]=D[N-1][K-1]+D[D-K][K] (动态转移方程)
编程如下:
N,K=[int(i) for i in input().split()]
dp=[None]*100
for i in range(100):
dp[i]=[0]*10
dp[0][0]=1
print(dp)
for i in range(1, N+1):
for j in range(1, K+1):
if i>=j:
dp[i][j]=dp[i-1][j-1]+dp[i-j][j]
print(dp[N][K])
4.给定一个正整数 n,将其拆分为至少两个正整数的和,并使这些整数的乘积最大化。 返回你可以获得的最大乘积。等价于剪绳子:(剑指offer14题)
//dp[i]表示:数字 i 拆分为至少两个正整数之和的最大乘积
n=int(input(""))
dp = [None]*(n+1)
for i in range(n+1):
dp[i]=0
#dp[i]表示整数i被拆分可以得到的最大乘积
dp[1] = 1
dp[2] = 1
#i可以被分为j与i-j
for i in range(3, n+1):
#j的范围是1 - i-1
for j in range(1, i):
#(i-j)*j不一定是最大乘积,因为i-j不一定大于dp[i - j]
dp[i]=max(dp[i], max(dp[i-j]*j, (i-j)*j))
print(dp[n])
5.最长递增子序列长度
思路:
def getResult(lis):
dp=[1]*len(lis)
n=len(lis)
for i in range(1,n):
for j in range(i):
# 如果lis[i]能缀在lis[j]后面的话,就dp[j]+1
if lis[i]>lis[j]:
#能缀则比较和现在比什么情况大
dp[i] = max(dp[i], dp[j]+1)
return max(dp)
6.礼物的最大价值
grid=[[1,2,3],
[4,5,6]]
m = len(grid)
n = len(grid[0])
dp = [[0 for j in range(n)] for i in range(m)]
dp[0][0] = grid[0][0]
for j in range(1,n):
dp[0][j] = dp[0][j-1] + grid[0][j]
for i in range(1,m):
dp[i][0] = dp[i-1][0] + grid[i][0]
for i in range(1,m):
for j in range(1,n):
dp[i][j] = max(dp[i-1][j],dp[i][j-1]) + grid[i][j]
print(dp[m-1][n-1])
7.三角形数的最大路径和
与矩阵路径类似。
def minimumTotal(triangle):
if (len(triangle) == 0 or triangle == None):
return 0
dp = [[0 for i in range(len(triangle[j]))] for j in range(len(triangle))]
dp[0][0] = triangle[0][0]
# dp数组中存的是每个坐标从顶往下的最短路径
# 每个数字的前一条路,要么从左上来,要么从右上来
# 每行第一列没有左上,只有右上
for i in range(len(triangle)):
for j in range(len(triangle[i])):
if j ==0:
dp[i][j] = dp[i-1][j] + triangle[i][j]
elif j==len(triangle[i])-1:
dp[i][-1] = dp[i-1][-1] + triangle[i][-1]
# 存最短路径:每个坐标的最短路径=其前序结点的最短路径+该结点本身
else:
# 左上坐标为row-1,column-1
# 右上坐标为row-1,column
dp[i][j] = min(dp[i-1][j-1], dp[i-1][j]) + triangle[i][j]
# 找出最后一行中最小的
length = len(triangle) - 1
minnum = 99999999
for i in range(len(dp[length])):
if (dp[length][i] < minnum):
minnum = dp[length][i]
return minnum
tri = [
[2],
[3,4],
[6,5,7],
[4,1,8,3]
]
print(minimumTotal(tri))
8.魔法森林
在一棵魔法森林中,每棵树都有攀比心里,每当魔法师指定一棵树时,指定的树高度不变,其他树的高度都会加1,知道所有树的高度都一样高时,所有树才会停止生长。魔法师当然不希望所所有的树疯狂生长,问魔法师知道需要操作多少次才能使得森立中的树停止生长.
思路:每次选择最高的树指定:定义一个判断操作是否停止的函数。 只要不停止就往下递归。(递归过程每次选择最大的树指定 其他树高度加1)
lis = [5,2,3,7]
def func(lis):
flag=0
for i in range(len(lis)-1):
if lis[i]!=lis[i+1]:
flag+=1
if flag!=0:
return True
else:
return False
def func2(lis):
if func(lis):
count = 0
max = lis[0]
flag = 0
for i in range(len(lis)):
if lis[i] > max:
max = lis[i]
flag = i
for i in range(len(lis)):
if i != flag:
lis[i] += 1
count = func2(lis)+1
return count
else:
return 0
print(func2(lis))
9.01背包问题(取不取)
for (int i = 1; i <= n; i++) {
for (int k = 1; k <= W; k++) {
// 存放 i 号物品(前提是放得下这件物品)
int valueWith_i = (k-weight[i-1] >= 0) ? (value[i-1]+dp[i-1][k-weight[i-1]]) : 0;
// 不存放 i 号物品
int valueWithout_i = dp[i - 1][k];
dp[i][k] = Math.max(valueWith_i, valueWithout_i);
}
}
10.完全背包问题(选几个)
零钱兑换:
给定不同面额的硬币 coins 和一个总金额 amount。编写一个函数来计算可以凑成总金额所需的最少的硬币个数。如果没有任何一种硬币组合能组成总金额,返回 -1。
amount=11
coins=[1,2,5]
n = len(coins)
# dp[i]表示amount=i需要的最少coin数
dp = [float("inf")] * (amount+1)
dp[0] = 0
for i in range(amount+1):
for j in range(n):
# 只有当硬币面额不大于要求面额数时,才能取该硬币
if coins[j] <= i:
dp[i] = min(dp[i], dp[i-coins[j]]+1)
print(dp)
# 硬币数不会超过要求总面额数,如果超过,说明没有方案可凑到目标值
if dp[amount]<=amount:
print(dp[amount])
else:
print(-1)