Dynamic Programming

本文深入探讨动态规划(DP)与回溯算法(Recursion)在解决算法问题时的精髓与区别,通过具体实例如三角形最小路径和、单词分割等,详细阐述DP与Recursion的推进方式、思维陷阱及应用技巧。同时,文章还介绍了二维动态规划的通用策略,以及如何通过内层推外层的方式解决复杂问题,例如单词分割与模式匹配等。最后,文章结合LeetCode中的一些难题,如背包问题、调整成本最小化等,进一步巩固读者对动态规划的理解,并提供了实用的解题思路。

这一类题目,对思考要求比较多。凡是要求思维的问题,一定是有迹可寻,有规律可以依靠的。

希望这一遍整理,能有长足长进。这是算法里都一块大骨头,一定要啃下来。


开始之前先说几句general的心得

dp,是从下往上到(tree),从后往前到(string)推进的。

recursion 正好相反。 从上往下,从前往后, 推进的。

recursion的思路往往是更为容易洞察的,而做dp很容易出现的一个思维陷阱就是,在发现recursion后再做dp,在分析dp推进关系的时候受recursion影响。

这里有一条原则要牢记,

dp在每一步是向前看的。

recursion在每一步是向后看的。


Triangle : **
一个窍门是从下往上推进,就可以不单独处理每行首末位了。
然后就是虽然这里是个三角形。。依然是2D DP
public class Solution {
    public int minimumTotal(List
  
   
    > triangle) {
        if(triangle == null || triangle.size() == 0 || triangle.get(0) == null || triangle.get(0).size() == 0)
            return 0;
        int r = triangle.size();
        int c = triangle.get(r-1).size();
        int[][] dp = new int[r][c];
        for(int i=0; i
    
     =0; i--){
            for(int j=0; j
     
    
   
  

Word Break  : ****

这个题目的DP, 和 Palindrome Partitioning II  放到一起领会。

这俩个问题的共通之处在于,递推关系是内层推外层。

palindrome里很直观  i  <-- i+1    j-1 -->  j

(i,j)  受 (i+1, j-1)影响。

i + 1 决定了 i 要从下向上

j - 1 决定了 j要从左向右

word break 也是一个道理,只不过关系更多种

i .....K..... j

(i , j) 受 (i,k)  与 (k+1,j) 一起影响


凡事外层靠内层推的2维DP, i 从下向上循环,每层里 j 从左到右。

Word Break 这个题也是显然的内层推外层,有些东西太显然反而被忽略。

拿 LeetCode 来说,我们最好要的结果就是LeetCode可以不可以break,固然 i(首char坐标) 最终是在0 位上的,那 i 肯定是从尾扫到头的。

不然你让i 从开始扫,那就成了先有LeetCode  能不能分,再有  eetCode能不能, 显然是不合理的。

所以这个题目也是回扫问题。


这个地方再多说一句,这个题recursion和dp乍一看有点矛盾。recursion明明是上来就是拆LeetCode。而仔细想正是相反才是一致。 DP从subproblem入手往LeetCode这个最终problem推,而recursion则反之,从LeetCode这个最终problem开始,不断的反代自身处理subproblem。 这就是一个从下向上,一个从上向下,所以recursion这是逻辑存在大量重复计算。

public class Solution {
    public boolean wordBreak(String s, Set
  
    wordDict) {
        if(s==null)
            return false;
        boolean[][] dp = new boolean[s.length()][s.length()];
        for(int i=s.length()-1;i>=0;i--){
            for(int j=i; j
   
  

先附一个以前做这个题目写的心得体会。

Word Break (Rugged Cross)


Wildcard Matching : ****

这DP之章被提前进行,就是因为这个题。

'?' Matches any single character.
'*' Matches any sequence of characters (including the empty sequence).

The matching should cover the entire input string (not partial).

The function prototype should be:
bool isMatch(const char *s, const char *p)

Some examples:
isMatch("aa","a") → false
isMatch("aa","aa") → true
isMatch("aaa","aa") → false
isMatch("aa", "*") → true
isMatch("aa", "a*") → true
isMatch("ab", "?*") → true
isMatch("aab", "c*a*b") → false
关于 ‘*’ match 任意sequence, 这个sequence 里character不一定是重复的。


DP思路:

1. 纬度定义 

二维DP, dp[i][j] 表示 p截至到i时候,跟s截至到j的匹配是否成功。

2. 递推关系

if( p.charAt(i) != '*')    :     

if(p.charAt(i) == s.charAt(j)   || p.charAt(i) == '?' ) : dp[i][j] = true

if(p.charAt(i) == '*')    :

dp[i][j] = dp[i-1][j] || dp[i-1][j-1] || dp[i-1][j-2] || ... || dp[i-1][0] = dp[i-1][j] || dp[i][j-1]


--------------------------------------------

(i-1 ,0)(i-1 ,1)... (i-1, j-2)(i-1, j-1)(i-1, j)

--------------------------------------------

(i, j-1)(i, j)

--------------------------------------------

3.矩阵初始化

当p取前0位(即空),即为空时, 出了s也不取时候,不论s几个,都无法match, 故第一行除了第一列为true, 其余列都是false

当s取前0位 (即空),第一列按照以下附值。

for(int i=1; i<=p.length();i++){
            if(p.charAt(i-1) == '*' && dp[i-1][0])
                dp[i][0] = true;

        }


这个题做了有四遍了。我总结一下思考上的技巧。 

1.把实际问题和矩阵的问题俩个角度结合起来,相互启发。

2.往往习惯是先从recursion入手,再分析dp。这里要警惕,不要把dp的分析思路让recursion误导。 记住一句话,dp是向前看的。



//improved
    public boolean isMatch(String s, String p) {
        boolean[][] dp = new boolean[p.length()+1][s.length()+1];
        dp[0][0] = true;
        for(int i=1; i<=p.length(); i++){
            if(p.charAt(i-1) == '*')
                dp[i][0] = dp[i-1][0];
        }
        
        for(int i=1; i<=p.length(); i++){
            for(int j=1; j<=s.length(); j++){
                if(p.charAt(i-1) == s.charAt(j-1) || p.charAt(i-1) == '?'){
                    dp[i][j] = dp[i-1][j-1];
                }else{
                    if(p.charAt(i-1) == '*'){
                        dp[i][j] = dp[i-1][j] || dp[i][j-1];
                    }
                }
            }
        }
        return dp[p.length()][s.length()];
    }
    
//older one

public class Solution {
    public boolean isMatch(String s, String p) {
        boolean[][] dp = new boolean[p.length()+1][s.length()+1];
        //初始化矩阵
        dp[0][0] = true;
        for(int i=1; i<=p.length();i++){
            if(p.charAt(i-1) == '*' && dp[i-1][0])
                dp[i][0] = true;
        }
        //初始化结束,开始递归
        for(int i=1;i<=p.length();i++){
            for(int j=1;j<=s.length();j++){
                if(p.charAt(i-1) != '*'){
                    if(dp[i-1][j-1] && (p.charAt(i-1) == s.charAt(j-1) || p.charAt(i-1) == '?')){
                        dp[i][j] = true;
                    }
                }else{
                    dp[i][j] = dp[i-1][j] || dp[i][j-1];
                }
            }
        }
        return dp[p.length()][s.length()];
    }
}


Palindrome Partitioning II : ****

这个题,值得反复做。我是做了三遍,还是出错。这是一个题里用了俩个DP。

public class Solution {
    public int minCut(String s) {
        if(s == null)
            return 0;
        boolean[][] dp = new boolean[s.length()][s.length()];
        isP(s,dp);
        int[] res = new int[s.length()];
        
        for(int i=0; i
 
  =0; i--){
            for(int j=i; j
  
   


Interleaving String : ***

这个题dp处理的角度挺独特。递推关系一旦找对了入手方向是比较直观多。

public class Solution {
    public boolean isInterleave(String s1, String s2, String s3) {
        // Note: The Solution object is instantiated only once and is reused by each test case.
        if (s1.length() + s2.length() != s3.length()) return false;
        boolean[][] dp = new boolean[s1.length() + 1][s2.length() + 1];
        dp[0][0] = true;
        for(int i = 1; i
     


Post Office Prob **** *

这个题,最难的一类dp了。 需要抽象二维dp矩阵,需要构造辅助变量矩阵,需要遍历找最优过渡变量。

这个题,和stock IV 神似

On one line there are n houses. Give you an array of integer means the the position of each house. Now you need to pick k position to build k post office, so that the sum distance of each house to the nearest post office is the smallest. Return the least possible sum of all distances between each village and its nearest post office.

public class Solution {
    /**
     * @param A an integer array
     * @param k an integer
     * @return an integer
     */
    //initialize the dist matrix, dist[i][j] means the shortest sum of 
    //distance for all houses between i and j(inclusive) to get to the best
    //located one post office(which is the mid point).
    int[][] init(int []A)  
    {  
        int n = A.length;
        int [][]dis = new int [n+1][n+1];
        for(int i = 1; i <= n; i++) {  
            for(int j = i+1 ;j <= n;++j)  
            {  
                int ii = i, jj = j;
                while(ii < jj){
                    dis[i][j] += (A[jj-- -1]-A[ii++ -1]);
                }
            } 
        }
        return dis; 
    } 
    
    public int postOffice(int[] A, int k) {
        // Write your code here
        int n = A.length;
        Arrays.sort(A);
        int[][] dis = init(A);
        int[][] dp = new int[n + 1][k + 1];
        if(n == 0 || k >= A.length)
            return 0;
        int ans = Integer.MAX_VALUE;
        for(int i = 0;i <= n;++i)  {
            dp[i][1] = dis[1][i];
        }
        //The dp core part, which reminds me of stock problem IV
        //But here, I didn't figure out of the way to track local_min 
        //So, have to use a third loop to get min
        for(int p = 2; p <= k; p++) {
            for(int i = p+1; i <= n; i++) {
                dp[i][p] = Integer.MAX_VALUE;
                for(int j = p+1; j < i; j++) {  
                    if(dp[i][p] > dp[j][p-1] + dis[j+1][i])  
                        dp[i][p] = dp[j][p-1] + dis[j+1][i];   
                }  
            }
        }
        return dp[n][k];
    }
}


之前leetcode里很多比较难的dp都是string背景下的。 而我对于dp的接触和理解也是从leetcode开始。一定程度上导致了我比较熟练string背景的题目。

但是dp是有更广泛的应用的,尤其是二维dp的实用领域是很广泛的。 上一题post office就是经典。 以及 stock IV


下面俩道题是lintcode  backpack类的俩道题。 需要好好体会的是,这俩题为何放一起, m值到底是什么。


Minimum Adjustment Cost

Given an integer array, adjust each integers so that the difference of every adjacent integers are not greater than a given number target.

If the array before adjustment is A, the array after adjustment isB, you should minimize the sum of |A[i]-B[i]|

Example

Given [1,4,2,3] and target = 1, one of the solutions is [2,3,2,3], the adjustment cost is 2 and it's minimal.

Return 2.

Note

You can assume each number in the array is a positive integer and not greater than 100.


Backpack

Given n items with size Ai, an integer m denotes the size of a backpack. How full you can fill this backpack?


    public int MinAdjustmentCost(ArrayList
     
       A, int target) {
        // write your code here
        int n = A.size();
        int m = 100;
        int[][] dp = new int[n+1][m];
        for(int j=0; j < m; j++){
            dp[0][j] = Math.abs(A.get(0) - j-1);
        }
        for(int i=1; i
      
       = A[0])
                dp[0][j] = A[0];
        }
        for(int i=1; i
       
        = A[i]){
                    dp[i][j] = Math.max(dp[i][j], dp[i-1][j-A[i]] + A[i]);
                }
            }
        }
        return dp[n-1][m];
    }
       
      
     

内容概要:本文档是一份关于交换路由配置的学习笔记,系统地介绍了网络设备的远程管理、交换机与路由器的核心配置技术。内容涵盖Telnet、SSH、Console三种远程控制方式的配置方法;详细讲解了VLAN划分原理及Access、Trunk、Hybrid端口的工作机制,以及端口镜像、端口汇聚、端口隔离等交换技术;深入解析了STP、MSTP、RSTP生成树协议的作用与配置步骤;在路由部分,涵盖了IP地址配置、DHCP服务部署(接口池与全局池)、NAT转换(静态与动态)、静态路由、RIP与OSPF动态路由协议的配置,并介绍了策略路由和ACL访问控制列表的应用;最后简要说明了华为防火墙的安全区域划分与基本安全策略配置。; 适合人群:具备一定网络基础知识,从事网络工程、运维或相关技术岗位1-3年的技术人员,以及准备参加HCIA/CCNA等认证考试的学习者。; 使用场景及目标:①掌握企业网络中常见的交换与路由配置技能,提升实际操作能力;②理解VLAN、STP、OSPF、NAT、ACL等核心技术原理并能独立完成中小型网络搭建与调试;③通过命令示例熟悉华为设备CLI配置逻辑,为项目实施和故障排查提供参考。; 阅读建议:此笔记以实用配置为主,建议结合模拟器(如eNSP或Packet Tracer)动手实践每一条命令,对照拓扑理解数据流向,重点关注VLAN间通信、路由选择机制、安全策略控制等关键环节,并注意不同设备型号间的命令差异。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值