编程笔试题※python实现※动态规划类/贪心

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]<dp[i-1][j]:
                i=i-1
    s1=''.join(lis)
    print(s1)

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)
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值