算法之递归与动态规划

1.最大连续乘积子数组(数组中的元素有正数有负数)

解法一:暴力法

两个for循环确定边界,可以找出所有数组的乘积然后在进行比较。时间O(N的平方)

解法二:动态规划

设f(i)是以第i个元素结尾的最大连续子数组。由于有负数的存在,那么f(i-1)就需要维护一个最大值和一个最小值(为负数),只有这样才能求出f(i)的最大。时间O(N),空间也可以常量,因为每一次的计算只需要用到上一次的信息而不需要以前的历史信息。

2.编辑距离

给定一个字符串使用最少的修改次数来变为目标串(有插入,替换,删除三种方式)

设dp(i,j)为字符串的前i个字符到目标串的前j个字符的最小代价。如果替换/修改操作dp(i,j)=dp(i-1,j-1)+1,如果是插入操作dp(i,j)=dp(i,j-1)+1,如果是删除操作dp(i,j)=dp(i-1,j)+1

ps:dp(i-1,j-1)代表把前i-1个字符编辑成j-1个字符的最小代价。然后把字符串a字符i替换为b的字符j

ps:dp(i,j-1)代表把前i个字符编辑成j-1个字符的最小代价。然后在给字符串a添加最后一个字符i

ps:dp(i-1,j)代表把前i-1个字符编辑成j个字符的最小代价。然后在给字符a删除最后一个字符i

ps:初始化时,要注意位置0的含义,代表空字符串!要特别注意这个空串,因为跟其他动态规划在这点不一样。

3.格子取数

给定矩阵,元素非负,从左上角走到右下角,每次只能向右和向下走。

设dp(i,j)为走到位置(i,j)的最优路径。位置(i,j)只能有两种途径,dp(i,j)=min(dp(i-1,j),dp(i,j-1))+Wi,j即可。

引申扩展:

格子取数加强版,如果格子中有障碍呢?

直接把dp(i,j)的位置,置为无穷大,因为该位置不可到达必须置无穷大,让此位置不可选。

4.交替字符串

给定s1,s2和s3三个字符串,判断s3是否可以由s1和s2交错组合而成。

首先不能排序因为会改变s1和s2的相对位置,

设dp(i,j)代表s1串的前i个字符和s2串的前j个字符是否可以交错组成s3的前i+j个字符。if s3[i+j]==s1[i] 且 dp[i-1][j]==True  或者  s3[i+j]==s2[j] 且 dp[i][j-1]==True   则dp[i][j] = True。

ps:暴力递归法,也是选a还是选b的问题。

5.换钱最少的硬币数

 

#include <iostream>
#include <algorithm>
#include <stdio.h>
using namespace std;
const int INF=100000;
int main()
{
    int i,n,temp,total,number;
    printf("请输入货币种类共多少个:\n");
    scanf("%d",&n);
    int value[n];       //存取每个纸币币值
    int num[n];         //存取每个纸币数量

    printf("请输入每种货币的币值,最小币值为1,保证有解:\n");
    for(int i=0;i<n;i++){
        scanf("%d",&temp);
        value[i]=temp;
    }
    printf("请输入需要找零总和:");
    scanf("%d",&total);
    int d[total+1];           //存取每个找零值所需要最小的纸币张数
    d[0]=0;
    for(int i=1;i<=total;i++){
            d[i]=INF;         //一定要附初值,之前认为反正挺大没关系,最后发现也可能初值很小,有影响
        for(int j=0;j<n;j++){
            if(i>=value[j]){   //边界值一定要注意
                d[i]=min(d[i],d[i-value[j]]+1);
            }
        }
    }
    printf("以下为找零最少纸币数:%d\n",d[total]);
    return 0;
}

物品体积总数不超过包的容量m时,能够得到的最大价值是多少?

01背包的思路:if j-arr[i]>0 则 dp[i][j] = max(dp[i-1][j],dp[i-1][j-arr[i]]+V[i])  选不选此物品

可以优化为dp[i]重量i可以获得的最大价值;

class Knapasack01{
    public :
        int knapsack01(int[] w,int[] v,int C){
        //C为背包容量
            int n = w.length();
            if(n == 0)
                return 0;
            //动态规划记忆数组。         
            int[][] dp = new int[n][C];
            //初始化第一行。
            for(int j=0 ; i<= C ; j++)
                dp[0][j] = (j>=w[0]?v[0]:0);
            
            for(int i=1 ; i<n ; i++)
                for(int j=0 ; j<C ; j++){
                    dp[i][j] = dp[i-1][j];
                    if(j>=w[i])
                        memo[i][j] = (int)Math.max(dp[i][j] , v[i]+dp[i][j-w[i]]);
                }
          return dp[n-1][C];  
        }   
}

6.最长递增子序列

注意:子序列和子数组的区别,一个不连续一个连续。

思路一:申请一个dp(i)代表以i结尾的最长子序列长度,需要两个for循环因为不连续所以每次确定都要在i位置以前在判断还有没有可以连接成为最长的序列

7.最长公共子序列

思路:设dp(i,j)为数组1在位置i和数组2在位置j的最长公共子序列,dp(i,j)在if s[i]!=s[j]时由dp[i-1][j]或dp[i][j-1]来决定,就是两个字符串需要谁移动一位,正是因为这样才会传递出来子序列。if s[i]==s[j]则为dp[i-1][j-1]+1。

扩展:最长公共子串,这个需要连续,所以dp(i,j)代表以s1[i]和s2[j]结尾的字符串的最长子序列。if s[i]!=s[j]时dp[i][j]应该置零,就是你不能在进行传递了;相等时应该加1!

8.数字字符串转换为字母组合的种数

str="11111"字母只能由一位或者两位来构成。

思路:类似斐波那契和跳台阶的问题f(n)=f(n-1)+f(n-2),但是条件受限两位数必须小于26一位数必须大于1。

9.最长不含重复字符的子字符串

思路:设dp(i)代表以第i个字符结尾的最长不重复子串,需要使用一个哈希表来记录第i个字符是否在以前出现过,如果没出现过dp(i)=dp(i-1)+1,,如果出现过而且他们之间的长度为d且大于dp(i-1)那么dp(i)=dp(i-1)+1,长度小于等于d的时候dp(i)就等于d。

10.股票的最大利润

给出一些时间点的价格,时间是有序排列。

思路一:使用暴力法,构造出所有的数对,找出最大的差异。

思路二:设dp(i)为在第i个时间段卖出的最大收益。需要记录前i时间段的最低价格,即可。

11.矩阵中最大的正方形

思路:dp[i][j] := 以 M[i][j] 为正方形**右下角**所能找到的最大正方形的边长。注意返回的是最小的边长,是个与的过程。

在一个由 0 和 1 组成的二维矩阵 M 内,找到只包含 1 的最大正方形,并返回其面积。

递推公式:dp[i][j] = min{dp[i-1][j], 
               dp[i][j-1], 
               dp[i-1][j-1]} + 1  若 M[i][j] == 1
         = 0                      否则

12.最长回文序列

思路:dp代表i到j的最大的回文序列,dp[i][j]=dp[i+1][j-1] + 2 if( s[i] == s[j])dp[i][j]=max(dp[i+1][j],dp[i][j-1]) if ( s[i] != s[j])

class Solution(object):
    def longestPalindromeSubseq(self, s):
        #dp[i][j]=dp[i+1][j-1] + 2 if( s[i] == s[j])
        #dp[i][j]=max(dp[i+1][j],dp[i][j-1]) if ( s[i] != s[j])
        n = len(s)
        dp = [ [0 for _ in range(n)]for _ in range(n) ]
        for i in range(n-1,-1,-1):
            dp[i][i] = 1
            for j in range(i+1,n):
                if(s[i]==s[j]):
                    dp[i][j] = dp[i+1][j-1] + 2
                else:
                    dp[i][j] = max(dp[i+1][j],dp[i][j-1])
        return dp[0][n-1]

扩展:最长回文子串

思路一:动态规划(dp代表以i和j为起点和结尾的最大回文子串,利用了回文子串的传递性划分成的子问题,n的平方,空间换时间),

class Solution(object):
    def longestPalindrome(self, s):
        #dp[i][j] = dp[i+1][j-1] (if s[i]==s[j])
        #dp[i][j] = 0 (if s[i]!=s[j])
        n = len(s)
        dp = [ [False for _ in range(n)]for _ in range(n) ]
        max_i = max_j = 0
        max_len = 1
        for i in range(n-1,-1,-1):
            dp[i][i] = True
            for j in range(i+1,n):
                if(s[i]==s[j] and ( dp[i+1][j-1] or j-i==1 )): #需要判断奇回文和偶回文
                    dp[i][j] = True
                    if max_len<(j-i+1):
                        max_len = j-i+1
                        max_i = i
                        max_j = j
        return s[max_i:(max_j+1)]

思路二:中心扩展法(以每个点为起始中心,有奇回文和偶回文两种,进行向外扩展)

13.给定数组,从数组中取出 n 个不复用的数的和为 sum

思路一:直接暴力搜索,每个数字都有选择和不选择两种情况,将最终的结果累加;

思路二:dp(i,j)代表前i个闭区间的数字组成j的种数,第i个数选还是不选的个数之和;

dp[i][j] = dp[i-1][j]+dp[i-1][j-v[i]] if(j-v[i] >= 0),dp[i][j] = dp[i-1][j] if(j-v[i]<0)/不选也是这样;

14.数组元素不相邻和最大

思路:dp(i)代表前i个元素不相邻和最大的值,dp[i] = max( dp[i-2]+arr[i], dp[i-1] ) 

扩展:如果有环

思路:代表首元素和尾元素不能同时选取,对前n-1个元素计算(不用尾元素)和后n-1个元素计算(不用头元素)取最大的值即可(比如:如果只有三个元素都不能取);

15.划分为 k 个相等的子集

思路:把元素随便划分,只要子集的和相等即可,先累加起来然后看是否可以整除如果可以在进行划分,递归的寻找等于val的子集,直到找到k个即满足条件,需要使用一个visited数组来进行记录已经找到的数字。

ps:无语了,python这代码过不了,但是java就可以过......

    def partitiontoEqualSumSubsets(self, nums, k):
        # write your code here
        n = len(nums)
        val = 0
        for i in range(n):
            val += nums[i]
        if (val % k != 0):
            return False
        else:
            val = val // k
            visited = [0 for _ in range(n)]
            return self.dfs(val, k, nums,visited, n, val)

    def dfs(self, val, k, nums,visited, n, const_val):#start可以不用,直接从0开始
        if (k == 0):
            return True
        if (val == 0):
            return self.dfs(const_val, k - 1, nums, visited , n, const_val)
        for i in range(n):
            if (visited[i] == 1):
                continue
            visited[i] = 1
            if (nums[i] - val<=0 and self.dfs(val - nums[i], k, nums,visited, n, const_val)):
                return True
            visited[i] = 0
        return False

16.矩阵中的最长递增路径

  • 思路:枚举每个点,寻找最长的递增数组长度,需要使用visited数组进行记录,暴力穷举太慢了
  • 空间换时间思路:可以利用len数组记录,已经枚举过该点的最长递增数组长度,先判断该点是不是递增数组长度计算过了!
    def longestIncreasingPath(self, matrix):
        """
        :type matrix: List[List[int]]
        :rtype: int
        """
        if(matrix==[]): return 0
        row = len(matrix)
        col = len(matrix[0])
        max_res = 0
        visited = [[0 for _ in range(col)]for _ in range(row) ]
        for i in range(row):
            for j in range(col):
                visited[i][j] = 1
                max_res = max(max_res, self.find(row-1,col-1,matrix,i,j,visited)+1 )
                visited[i][j] = max_res
        return max_res

    def find(self,row,col,matrix,i_row,i_col,visited):
        flag1 =flag2 = flag3 = flag4 = False
        tmp = 0
        if(i_row>0 and not visited[i_row-1][i_col] and i_row<=row and matrix[i_row-1][i_col]>matrix[i_row][i_col]):
            visited[i_row-1][i_col] = 1
            l1 = self.find(row,col,matrix,i_row-1,i_col,visited)+1
            flag1 = True
            tmp = max(l1,tmp)
            visited[i_row-1][i_col] = 0
        if(i_row<row and not visited[i_row+1][i_col] and i_row>=0 and matrix[i_row+1][i_col]>matrix[i_row][i_col]):
            visited[i_row+1][i_col] = 1
            l2 = self.find(row,col,matrix,i_row+1,i_col,visited)+1
            flag2 = True
            tmp = max(l2,tmp)
            visited[i_row+1][i_col] = 0
        if(i_col>0 and not visited[i_row][i_col-1] and i_col<=col and matrix[i_row][i_col-1]>matrix[i_row][i_col]):
            visited[i_row][i_col-1] = 1
            l3 = self.find(row,col,matrix,i_row,i_col-1,visited)+1
            flag3 = True
            tmp = max(l3,tmp)
            visited[i_row][i_col-1] = 0
        if(i_col<col and not visited[i_row][i_col+1] and  i_col>=0 and matrix[i_row][i_col+1]>matrix[i_row][i_col]):
            visited[i_row][i_col+1] = 1
            l4 = self.find(row,col,matrix,i_row,i_col+1,visited)+1
            flag4 = True
            tmp = max(l4,tmp)
            visited[i_row][i_col+1] = 0
        if(not flag1 and not flag2 and not flag3 and not flag4):
            return 0
        return tmp

17.机器人的运动范围

地上有一个m行和n列的方格。一个机器人从坐标0,0的格子开始移动,每一次只能向左,右,上,下四个方向移动一格,但是不能进入行坐标和列坐标的数位之和大于k的格子。 例如,当k为18时,机器人能够进入方格(35,37),因为3+5+3+7 = 18。但是,它不能进入方格(35,38),因为3+5+3+8 = 19。请问该机器人能够达到多少个格子?

思路:递归的每次从四个方向进入,如果和符合条件的话。

18.给定一个字符串 s,将 s 分割成一些子串,使每个子串都是回文串 返回 s 所有可能的分割方案

思路一:递归,每次确定一位,每一位都是可分和不可分两种情况;

思路二:动态规划,dp(i)代表前i个元素划分的种类;

def cece(s: str) -> List[List[str]]:
        dp = [[] for _ in range(len(s) + 1)]
        dp[-1] = [[]]
        for i in range(len(s) - 1, -1, -1):
            for j in range(i + 1, len(s) + 1):
                if s[i:j] == s[i:j][::-1]:
                    for each in dp[j]:
                        dp[i].append([s[i:j]] + each)
        return dp[0]

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值