leetcode 2/7-2/13做题笔记

不同的子序列(HARD)

在这里插入图片描述

  • 记忆化搜索或者后缀DP即可

记忆化搜索

class Solution {

    public int dfs(int[][] dp,int i,int j,String s,String t,int len_s,int len_t)
    {
        if(i >= s.length())
            return 1;
        if(j >= t.length())
            return 0;
        if(dp[i][j] != -1)
            return dp[i][j];
        if(len_s - i > len_t - j)
        {
            dp[i][j] = 0;
            return 0;
        }
        int b = dfs(dp,i,j+1,s,t,len_s,len_t);
        if(s.charAt(i) == t.charAt(j))
        {
            dp[i][j] = dfs(dp,i+1,j+1,s,t,len_s,len_t) + b;
            return dp[i][j];
        }
        dp[i][j] = b;
        return b;
    }


    public int numDistinct(String s, String t) {
        int len_s = s.length();
        int len_t = t.length();
        int[][] dp = new int[len_t][len_s];
        for(int i = 0;i<dp.length;i++)
            Arrays.fill(dp[i],-1);
        return dfs(dp,0,0,t,s,len_t,len_s);
    }

    public static void main(String[] args)
    {
        int a = new Solution().numDistinct("rabbbit","rabbit");
        System.out.println(a);
    }
}

后缀DP

    public int numDistinct(String s, String t) {
        int len_s = s.length();
        int len_t = t.length();
        int[] pre = new int[s.length()];
        for(int  i = pre.length - 1;i>=0;i--)
        {
            if(i == pre.length - 1)
                pre[i] = s.charAt(i) == t.charAt(len_t - 1)?1:0;
            else pre[i] = pre[i + 1] + (s.charAt(i) == t.charAt(len_t - 1)?1:0);
        }
        int[] dp = new int[s.length()];
        for(int i = len_t - 2;i>=0;i--)
        {
            for(int j  =  len_s - 1 ;j>=0;j--)
            {
                if(j == len_s - 1)
                    dp[j] = 0;
                else
                {
                    dp[j] = (t.charAt(i) == s.charAt(j)?pre[j + 1]:0) + dp[j + 1];
                }
            }
            System.arraycopy(dp,0,pre,0,pre.length);
        }
        return pre[0];
    }

文本左右对齐(HARD)

在这里插入图片描述
在这里插入图片描述

  • 模拟即可
class Solution {
    public List<String> fullJustify(String[] words, int maxWidth) {
        int i = 0;
        List<String> a = new ArrayList<>();
        while(i < words.length)
        {
            int total_count = words[i].length();
            int w_len = words[i].length();
            int j = i + 1;
            while(j < words.length && total_count <= maxWidth)
            {
                total_count += words[j].length();
                total_count += 1;
                w_len += words[j].length();
                j++;
            }
            StringBuilder s = new StringBuilder();
            if(j >= words.length && total_count <= maxWidth)   //最后一行
            {
                 for(int k = i;k<j;k++)
                 {
                    s.append(words[k]);
                    if(k != j - 1)
                        s.append(' ');
                 }
                 s.append(" ".repeat(maxWidth - total_count));
                 a.add(s.toString());
            }
            else     //没有最后一行
            {
                j--;
                w_len -= words[j].length();   //单词数目
                j--;
                if(i == j)
                {
                    a.add(words[j] + " ".repeat(maxWidth - words[j].length()));
                }
                else
                {
                    int e_s = (maxWidth - w_len) / (j - i);   //每个空格数目
                    int extra = (maxWidth - w_len) - (e_s * (j - i));  //左侧额外添加空格的数目
                    for(int k = i;k<=j - 1;k++)
                    {
                        s.append(words[k]);
                        s.append(k - i + 1 <= extra ? " ".repeat(e_s + 1):" ".repeat(e_s));
                    }
                    s.append(words[j]);
                    a.add(s.toString());
                }
            }
            i = j + 1;
        }
        return a;
    }
}

复习-中缀表达式转换为逆波兰表达式

  • 遇到操作数直接输出
  • 遇到左括号入栈
  • 遇到右括号弹栈直到遇到左括号(左括号也跟着弹栈)
  • 遇到运算符弹栈直到栈空或者栈顶运算符的优先级高于栈中其他操作符的优先级
  • 遍历完成后将栈中所有元素弹空
    在这里插入图片描述在这里插入图片描述

基本计算器(HARD)仅含加减,数字,括号的情况(思路是参考官方的,不使用逆波兰表达式方法)在这里插入图片描述

在这里插入图片描述

  • 由于仅存在加法,减法,括号,考虑括号展开法则。
  • 对每对括号里的部分设置"符号状态"。使用栈保存当前的符号状态。
  • 对于一对括号掌管的部分而言,遇到左括号就在栈顶设置一个当前"符号状态"。
  • 对没进入新的括号的部分,如果遇到+,则使用当前栈顶的符号状态赋予当前操作数,否则使用当前符号状态的相反数赋予当前操作数。
  • 遇到右括号就将栈顶的符号弹出表明该对括号掌管的符号状态已经结束。
  • 整个表达式可以看作是0 + (表达式)的形式。故初始的符号状态就是正。
class Solution {
    public int calculate(String s) {
        int sign = 1;
        Stack<Integer> op = new Stack<>();
        int i = 0;
        op.push(1);
        long num = 0;
        long ret =0 ;
        while(i < s.length())
        {
            char ch = s.charAt(i);
            if(ch == '+')
            {
                sign = op.peek();   //使用当前的符号状态
               
            }
            else if(ch == '-')
            {
                sign = -op.peek();  //符号状态与栈顶符号状态即当前符号状态相反
            }
            else if(ch == '(')   //保存当前符号状态
            {
                op.push(sign);
            }
            else if(ch == ')')
            {
                op.pop();   //当前括号掌管的sign结束
                
            }
            else if(ch != ' ')
            {
                while(i<s.length() && '0'<=s.charAt(i)&&s.charAt(i)<='9')
                {
                    num = num * 10 + (s.charAt(i)  -  '0');
                    i++;
                }
                ret += sign * num;
                num = 0;
                continue;
            }
            i++;
        }
        return (int)ret;
    }
}

分发糖果(HARD)

在这里插入图片描述
在这里插入图片描述

拓扑排序(效率低,没有充分利用相邻关系)

  • 建图:每人作为一个结点,如果结点A必须比结点B必须得到的糖果多则从结点B到结点A连一条边。该图为有向无环图(但不一定连通)。
  • 对原图中每个入度为0的结点以起始点拓扑排序,求出结果。
class Solution {
    //topological sort
    public int candy(int[] ratings) {
        int[] dis = new int[ratings.length];
        Arrays.fill(dis,-1);
        //邻接表
        Map<Integer,List<Integer>> a = new HashMap<>();
        //入度
        int[] ind = new int[ratings.length];
        //建图
        for(int i  =  0;i<ratings.length;i++)
        {
            List<Integer> b = new ArrayList<>();
            if(i + 1 < ratings.length && ratings[i + 1] > ratings[i]) {
                b.add(i + 1);
                ind[i+1]++;
            }
            if(i  - 1 >= 0 && ratings[i - 1] > ratings[i]) {
                b.add(i - 1);
                ind[i-1]++;
            }
            a.put(i,b);
        }
        for(int i = 0;i<ratings.length;i++)
        {
            if(dis[i] == -1 && ind[i] == 0)
            {
                dis[i] = 1;
                Queue<Integer> q = new LinkedList<>();
                q.add(i);
                while(!q.isEmpty())
                {
                    int v = q.poll();
                    for(int k : a.get(v))
                    {
                        dis[k] = Math.max(dis[v]+1,dis[k]);
                        ind[k]--;
                        if(ind[k] == 0)
                        {
                            q.add(k);
                        }
                    }
                }
            }
        }
        int ans = 0;
        for(int i = 0;i<dis.length;i++)
            ans += dis[i];
        return ans;
    }
}

左到右,右到左两次遍历( O ( n ) 空 间 O(n)空间 O(n)

  • 按结点编号排成链式,仅相邻两点存在边
  • 先从左向右遍历从左到右的边,得到的解就是最优解的下界
  • 再从右向左遍历从右到左的边,再次得到一个最优解的下界
  • 将每个结点两个最优解下界取最大值就是最优解。只需证明得到的解是可行解即可。对第i个结点分类讨论:
    • r a t e [ i − 1 ] > r a t e [ i ] , r a t e [ i ] < r a t e [ i + 1 ] rate[i - 1] > rate[i],rate[i] < rate[i+1] rate[i1]>rate[i],rate[i]<rate[i+1],则candy[i]=1,从左到右遍历使得最终 c a n d y [ i + 1 ] > = 1 + 1 = 2 , c a n d y [ i − 1 ] candy[i+1] >=1+1=2,candy[i-1] candy[i+1]>=1+1=2,candy[i1]同理。
    • r a t e [ i ] > r a t e [ i − 1 ] , r a t e [ i ] > r a t e [ i + 1 ] rate[i] > rate[i-1],rate[i]>rate[i+1] rate[i]>rate[i1],rate[i]>rate[i+1],则i+1仅在从右到左才有可能大于1.而从右到左遍历又使得 c a n d y [ i ] > c a n d y [ i + 1 ] candy[i] > candy[i+1] candy[i]>candy[i+1]。i结点同理
    • r a t e [ i − 1 ] < r a t e [ i ] < r a t e [ i + 1 ] rate[i-1]<rate[i] < rate[i+1] rate[i1]<rate[i]<rate[i+1] r a t e [ i − 1 ] rate[i-1] rate[i1]仅在从左到右才可能大于1,而从左到右 c a n d y [ i ] > c a n d y [ i − 1 ] candy[i] > candy[i-1] candy[i]>candy[i1]。对 c a n d y [ i + 1 ] candy[i+1] candy[i+1]无论是从左到右,从右到左的结果均大于 c a n d y [ i ] candy[i] candy[i]
    • 其余情况均对称
class Solution {
    public int candy(int[] ratings) {
        int[] left = new int[ratings.length];
        int[] right = new int[ratings.length];
        left[0] = 1;
        for(int i = 1;i<ratings.length;i++)
        {
            if(ratings[i] > ratings[i-1])
                left[i] = left[i - 1] + 1;
            else left[i] = 1;
        }
        right[ratings.length  - 1]= 1;
        int ans = Math.max(right[ratings.length - 1],left[ratings.length - 1]);
        for(int i = ratings.length - 2;i>=0;i--)
        {
            if(ratings[i] > ratings[i + 1])
                right[i] = right[i+1] + 1;
            else right[i] = 1;
            ans += Math.max(right[i],left[i]);
        }
        return ans;
    }
}

常数空间做法

  • 如何贪心的尽可能小得分糖果。
  • 结点1分1个开始,后面一个结点得rate大于前一个结点,后面结点个数为前一个结点个数加1.否则,考察最长递减序列的情况即可(当当前结点的数量减为0时,应该将该递减序列的哪些部分糖果加1)。
class Solution {
    public int candy(int[] ratings) {

        int pre =  1;
        int lg_dec = 1;  //当前最长递减子序列长度
        int ans = 1;
        int dif = 0;
        for(int i = 1;i<ratings.length;i++)
        {
            if(ratings[i] == ratings[i - 1])
            {
                lg_dec = 1;
                pre = 1;
                ans += 1;
                dif = 0;
            }
            else if(ratings[i] < ratings[i - 1])
            {
                if(lg_dec == 1)
                {
                    ans+=(dif == 0?2:1);
                    if(dif == 0)
                        dif = 1;
                }
                else
                {
                    ans += (dif == 1?lg_dec:lg_dec - 1) + 1;
                    if(dif != 1)
                        dif--;
                }
                pre = 1;
                lg_dec++;
            }
            else
            {
                pre++;
                ans += pre;
                lg_dec = 1;
                dif = pre - 1;
            }
        }
        return ans;
    }
}

代码优化

  • Inc始终记录可能进入递减字串的第一个数值大小
class Solution {
    public int candy(int[] ratings) {

        int pre =  1;
        int ans = 1;
        int inc = 1;
        int dif = 0;
        for(int i = 1;i<ratings.length;i++)
        {
            if(ratings[i] == ratings[i - 1])
            {
                pre = 1;
                ans += 1;
                inc = 1;
                dif = 0;
            }
            else if(ratings[i] < ratings[i - 1])
            {
                dif++;
                if(dif == inc)
                    dif++;
                pre = 1;
                ans+=dif;
            }
            else
            {
                pre++;
                ans += pre;
                inc = pre;
                dif = 0;
            }
        }
        return ans;
    }
}

复习:KMP算法

  • KMP算法模板
class Solution {
    //haystack中寻找needle出现的第一个位子
    public int strStr(String haystack, String needle) {
        //空串
        if(needle.equals(""))
            return 0;
        //next数组
        int[] next = new int[needle.length()];
        int j,k;
        j=0;k=-1;
        next[0]=-1;//第一个字符前无字符串,给值-1
        while (j<needle.length()-1)
        {
            if (k==-1 || needle.charAt(j)==needle.charAt(k))
            {
                j++;k++;
                next[j]=k;
            }
            else
            {
                k=next[k];
            }
        }
        // 模式匹配
        j = k = 0;
        while(j < haystack.length() && k < needle.length())
        {
            if(k == -1 || haystack.charAt(j) == needle.charAt(k))
            {
                k++;
                j++;
            }
            else
                k = next[k];
        }
        if(k >= needle.length())
        {
            return j - needle.length();
        }
        else return -1;
    }
}

分隔回文串(HARD)

在这里插入图片描述

  • 直接dp求解回文串会超时( O ( n 2 ) O(n^2) O(n2)),但我们是求解最长前缀回文串,dp求解了任意字串是否回文串

KMP算法的MOTIVATION

  • 最长前缀回文串,逆序后该串与自身相同,且处于原始串s的前缀。因此将s逆序放在后面,利用KMPnext数组的性质求解最长真前缀真后缀串
  • 但为避免产生真前缀串或真后缀串同时包含原始串和逆序串的字符,拼接中央插入字符#

构造思路

  • 将字符串s逆序得到 s , s^, s,,构造字符串 s # s , a s\#s^,a s#s,a,#是字符#,a是任意字符。问题转换为求最后字符a的next数组值(即最长真前缀串和最长真后缀串),此值减1
    public String shortestPalindrome(String s) {
        StringBuilder s1 = new StringBuilder();
        s1.append(s); s1.append('#');
        for(int i = s.length() - 1;i>=0;i--)
            s1.append(s.charAt(i));
        s1.append('a');
        s = s1.toString();
        int[] next = new int[s.length()];
        int j,k;
        j=0;k=-1;
        next[0]=-1;//第一个字符前无字符串,给值-1
        while (j<s.length()-1)
        {
            if (k==-1 || s.charAt(j)==s.charAt(k)) 	
            {
                j++;k++;
                next[j]=k;
            }
            else
            {
                k=next[k];
            }
        }

        int len  = (s.length() - 2) / 2;
        return s.substring(len + 1,len + 1 + len - next[next.length - 1]) + s.substring(0,len);
    }

ROLLING哈希映射

  • motivation:前缀:考虑滑动窗口
  • 枚举每个可能前缀位置。原始字符串前缀的hashing值,逆转字符串对应后缀的hashing值。认为hashing相等字符串就相等,选取合适的function使得不碰撞。
  • function的选取:将字符串看成base进制数。大于字符集大小(种类数)作为base
    f u n c t i o n [ s ] = ( s [ 0 ] ∗ b a s e + s [ 1 ] ∗ b a s e 2 + ⋯ + s [ n − 1 ] ∗ b a s e n − 1 ) % m o d function[s] = (s[0] * base + s[1] * base^2 + \cdots + s[n-1] * base^{n-1} ) \% mod function[s]=(s[0]base+s[1]base2++s[n1]basen1)%mod
    mod选取字符串平方级别的质数。碰撞概率小。(只能是通过测试用例)
class Solution {
    public String shortestPalindrome(String s) {
        long base = 131, mod = 1000000007;
        long hash1 =  0,hash2 = 0;
        int max_len = 0;
        long a = 1;
        StringBuilder ans = new StringBuilder(s);
        ans.reverse();
        for(int j = 0,k = s.length() - 1;j<s.length();j++,k--)
        {
            hash1 = ((base * hash1) % mod + s.charAt(j) % mod) % mod;
            hash2 = (((ans.charAt(k) % mod) * a) % mod + hash2) % mod;
            if(hash1 == hash2)
                max_len = j + 1;
            a = (a  * (base % mod)) % mod;
        }
        ans = new StringBuilder();
        for(int j = s.length() - 1;j>=max_len;j--)
            ans.append(s.charAt(j));
        ans.append(s);
        return ans.toString();
    }
}

给表达式添加运算符(HARD)

在这里插入图片描述

方案1(击败12%)

  • 考虑第一个运算符后的子问题即可
class Solution {

    //求解字符串s从i开始的所有解
    // m_acc代表当前得到的乘积累积结果
    // fan表示添加的运算符是否取反
    // target是目标值
    // a表示当前得到的结果
    public void dfs(long m_acc,int i,String s,List<String> ans,boolean fan,long target,Stack<String> a,char lastch)
    {

        long acc = 0;
        //第一个运算符出现的位置
        int j;
        for( j = i + 1;j<=s.length()-1;j++)
        {
            acc = acc * 10 + (s.charAt(j - 1) - '0');
            if(lastch == '*')
            {
                a.push(acc + "*");
                dfs(m_acc  * acc,j,s,ans,fan,target,a,'*');//这里的运算符也是乘号
                a.pop();
                a.push(acc + (fan?"+":"-"));   //这里的运算符是加号
                dfs(1,j,s,ans,fan,target - m_acc * acc,a,'+');
                a.pop();
                a.push(acc + (fan ? "-" : "+"));  //这里的运算符是减号
                dfs(1,j,s,ans,!fan,m_acc * acc - target,a,'-');
                a.pop();
            }
            else
            {
                a.push(acc + "*");
                dfs(acc,j,s,ans,fan,target,a,'*');//这里的运算符是乘号
                a.pop();
                a.push(acc + (fan?"+":"-"));   //这里的运算符是加号
                dfs(1,j,s,ans,fan,target - acc,a,'+');
                a.pop();
                a.push(acc + (fan?"-":"+"));   //这里的运算符是减号
                dfs(1,j,s,ans,!fan,acc - target,a,'-');
                a.pop();
            }
            if(j == i + 1 && s.charAt(j - 1) == '0')
                return;
        }
        acc = acc * 10 + (s.charAt(j - 1) - '0');
        if((lastch == '*' && m_acc * acc == target) || (lastch!='*' && acc == target))
        {
            StringBuilder q = new StringBuilder();
            for(int k = 0;k<a.size();k++)
                q.append(a.get(k));
            q.append(acc);
            ans.add(q.toString());
        }
    }



    public List<String> addOperators(String num, int target) {
        List<String> ans  = new ArrayList<>();
        Stack<String> a = new Stack<>();
        dfs(1,0,num,ans,true,target,a,'+');
        return ans;
    }

    public static void main(String[] args)
    {
        List<String> ans  = new Solution().addOperators("105",5);
        for(String s:ans)
        {
            System.out.println(s);
        }
    }
}

优化-考虑不用上述stack转而用index记录最后一个值(官方)

class Solution {
    int n;
    String num;
    int target;
    List<String> ans;

    public List<String> addOperators(String num, int target) {
        this.n = num.length();
        this.num = num;
        this.target = target;
        this.ans = new ArrayList<String>();
        StringBuffer expr = new StringBuffer();
        backtrack(expr, 0, 0, 0);
        return ans;
    }

    public void backtrack(StringBuffer expr, int i, long res, long mul) {
        if (i == n) {
            if (res == target) {
                ans.add(expr.toString());
            }
            return;
        }
        int signIndex = expr.length();
        if (i > 0) {
            expr.append(0); // 占位,下面填充符号
        }
        long val = 0;
        // 枚举截取的数字长度(取多少位),注意数字可以是单个 0 但不能有前导零
        for (int j = i; j < n && (j == i || num.charAt(i) != '0'); ++j) {
            val = val * 10 + num.charAt(j) - '0';
            expr.append(num.charAt(j));
            if (i == 0) { // 表达式开头不能添加符号
                backtrack(expr, j + 1, val, val);
            } else { // 枚举符号
                expr.setCharAt(signIndex, '+');
                backtrack(expr, j + 1, res + val, val);
                expr.setCharAt(signIndex, '-');
                backtrack(expr, j + 1, res - val, -val);
                expr.setCharAt(signIndex, '*');
                backtrack(expr, j + 1, res - mul + mul * val, mul * val);
            }
        }
        expr.setLength(signIndex);
    }
}


周赛T4-数组的最大与和(HARD)

在这里插入图片描述

状态压缩DP(代码冗长 不太好)

  • 最优子结构显然; d p [ i ] [ j ] dp[i][j] dp[i][j]表示直到第i个元素,状态为j获得的最大与和
  • 状态j的定义:高numslots比特记录是否有第二个物品在对应箱子,低位比特记录是否有第一个物品在对应箱子
  • 预先计算在有i+1个元素时,所有状态j的可能值
class Solution {
    public int maximumANDSum(int[] nums, int numSlots) {
        int[][]dp = new int[nums.length][1 << (2 * numSlots)];
        List<Integer>[] a = new List[2 * numSlots + 1];
        for(int i = 0;i<a.length;i++)
            a[i] = new ArrayList<Integer>();   //已经放了多少个数
        for(int i = 0;i<(1 << (2 * numSlots));i++)
        {
            boolean flag = true;
            int count_1 = 0;
            for(int j  =  0;j<numSlots;j++)
            {
                if (((i >> j) & 1) == 0 && (((i >> (j + numSlots)) & 1 )!= 0))
                {
                    flag = false;
                    break;
                }
                else
                {
                    count_1 += (((i >> j) & 1) + ((i >> (j + numSlots)) & 1 ));
                }
            }
            if(flag)
            {
                a[count_1].add(i);
            }
        }
        int ans  =  Integer.MIN_VALUE;
        for(int i : a[1])
        {
            for(int k = 0;k<numSlots;k++)
            {
                if(((i >> k) & 1) == 1)
                    dp[0][i] = Math.max(dp[0][i],dp[0][i] & (k + 1));
            }
            dp[0][i] = nums[0] & 1;
        }
        for(int i = 1;i<nums.length;i++)
        {
            for(int j : a[i + 1])   //尝试在桶子中除去一个
            {
                int v = Integer.MIN_VALUE;
                for(int k = 0;k<numSlots;k++)
                {
                    if(((j >> (k + numSlots) )& 1) == 1)
                        v = Math.max(v, (nums[i] & (k+1))+ dp[i-1][j - (1 << (k + numSlots))]);
                    else if(((j >> k) & 1) == 1)
                        v = Math.max(v,(nums[i] & (k+1)) + dp[i-1][j - (1 << k)]);
                }
                dp[i][j] = v;
            }
        }
        for(int j : a[nums.length])
            ans = Math.max(ans,dp[nums.length - 1][j]);
        return ans;
    }
}

代码优化

  • 注意使用2bits位可以将其看作2n个篮子。只需让相邻的篮子编号一致即可。从低到高第j位位子为1则篮子编号 j / 2 + 1 j/2 + 1 j/2+1
  • 省略了状态记录

三进制压缩

  • 利用三进制数字代替二进制压缩。从低到高代表对应编号篮子中有几个。0,1,2三种状态
  • f [ i , m a s k ] f[i,mask] f[i,mask]表示处理到第i个,状态mask的最优解。
class Solution:
    def maximumANDSum(self, nums: List[int], numSlots: int) -> int:
        
        @lru_cache(None)
        def f(i, mask):
            if i < 0:
                return 0
            t, w, res = mask, 1, 0
            for k in range(1, numSlots + 1):
                if t % 3:
                    res = max(res, f(i-1, mask-w) + (k & nums[i]))
                t, w = t // 3, w * 3
            return res
        
        return f(len(nums) - 1, 3**numSlots-1)



匹配与最大流

  • 将num[i]分配到第j个篮子 有点像匹配问题
  • 最小费用最大流思路
    在这里插入图片描述

SETLENGTH方法

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 好的,我来用中文回复这个链接:https://leetcode-cn.com/tag/dynamic-programming/ 这个链接是 LeetCode 上关于动态规划的题目集合。动态规划是一种常用的算法思想,可以用来解决很多实际问题,比如最长公共子序列、背包问题、最短路径等等。在 LeetCode 上,动态规划也是一个非常重要的题型,很多题目都需要用到动态规划的思想来解决。 这个链接里包含了很多关于动态规划的题目,按照难度从简单到困难排列。每个题目都有详细的题目描述、输入输出样例、题目解析和代码实现等内容,非常适合想要学习动态规划算法的人来练习和提高自己的能力。 总之,这个链接是一个非常好的学习动态规划算法的资源,建议大家多多利用。 ### 回答2: 动态规划是一种算法思想,通常用于优化具有重叠子问题和最优子结构性质的问题。由于其成熟的数学理论和强大的实用效果,动态规划在计算机科学、数学、经济学、管理学等领域均有重要应用。 在计算机科学领域,动态规划常用于解决最优化问题,如背包问题、图像处理、语音识别、自然语言处理等。同时,在计算机网络和分布式系统中,动态规划也广泛应用于各种优化算法中,如链路优化、路由算法、网络流量控制等。 对于算法领域的程序员而言,动态规划是一种必要的技能和知识点。在LeetCode这样的程序员平台上,题目分类和标签设置十分细致和方便,方便程序员查找并深入学习不同类型的算法。 LeetCode动态规划标签下的题目涵盖了各种难度级别和场景的问题。从简单的斐波那契数列、迷宫问题到可以用于实际应用的背包问题、最长公共子序列等,难度不断递进且话题丰富,有助于开发人员掌握动态规划的实际应用技能和抽象思维模式。 因此,深入LeetCode动态规划分类下的题目学习和练习,对于程序员的职业发展和技能提升有着重要的意义。 ### 回答3: 动态规划是一种常见的算法思想,它通过将问题拆分成子问题的方式进行求解。在LeetCode中,动态规划标签涵盖了众多经典和优美的算法问题,例如斐波那契数列、矩阵链乘法、背包问题等。 动态规划的核心思想是“记忆化搜索”,即将中间状态保存下来,避免重复计算。通常情况下,我们会使用一张二维表来记录状态转移过程中的中间值,例如动态规划求解斐波那契数列问题时,就可以定义一个二维数组f[i][j],代表第i项斐波那契数列中,第j个元素的值。 在LeetCode中,动态规划标签下有众多难度不同的问题。例如,经典的“爬楼梯”问题,要求我们计算到n级楼梯的方案数。这个问题的解法非常简单,只需要维护一个长度为n的数组,记录到达每一级楼梯的方案数即可。类似的问题还有“零钱兑换”、“乘积最大子数组”、“通配符匹配”等,它们都采用了类似的动态规划思想,通过拆分问题、保存中间状态来求解问题。 需要注意的是,动态规划算法并不是万能的,它虽然可以处理众多经典问题,但在某些场景下并不适用。例如,某些问题的状态转移过程比较复杂,或者状态转移方程中存在多个参数,这些情况下使用动态规划算法可能会变得比较麻烦。此外,动态规划算法也存在一些常见误区,例如错用贪心思想、未考虑边界情况等。 总之,掌握动态规划算法对于LeetCode的学习和解题都非常重要。除了刷题以外,我们还可以通过阅读经典的动态规划书籍,例如《算法竞赛进阶指南》、《算法与数据结构基础》等,来深入理解这种算法思想。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值