遞歸超時怎么破?——動態規划入門


引用一下別的大佬寫的介紹還有知乎上的十問十答

搞清楚什么是動態規划,和什么時候用動態規划。

p.s.百度百科和算法數上那一大堆看完也沒什么意思,不如從實例入手。掌握分析遞推關系才是王道。

 集合存儲狀態+狀態轉移方程

超級樓梯

共兩種爬樓方式——一次上一個台階&一次上兩個台階,問上到第n階台階的方法共多少種。

設狀態dp[i]為上i階台階的方法種數,dp[1]=1;dp[2]=1;
狀態轉移方程 dp[i]=dp[i-1]+dp[i-2];//上一階和兩階

有了該遞推式,我們就不用遞歸暴力解決了。(遞歸開銷是真的大

不同路徑

dp[i][j]為到單元格(i,j)的方法數,dp[0][]=1;dp[][0]=1;
dp[i][j]=dp[i-1][j]+dp[i][j-1];//向下走和向右走

  

    public int uniquePaths(int m, int n) {
        int[][] dp = new int[m][n];        
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                if (i == 0 || j == 0)
                    dp[i][j] = 1;
                else {
                    dp[i][j] = dp[i - 1][j] + dp[i][j - 1];
                }
            }
        }
        return dp[m - 1][n - 1];        
    }

 進階:不同路徑 II

    public int uniquePathsWithObstacles(int[][] obstacleGrid) {
        int row=obstacleGrid.length;
        int col=obstacleGrid[0].length;
        int[][] dp=new int[row][col];
        if(obstacleGrid[0][0]==1){ 
            return 0;
        }
        for(int i=0;i<row;i++){
            for(int j=0;j<col;j++){
                if(obstacleGrid[i][j]==0){
                    if(i==0&&j==0){ 
                        dp[i][j]=1;
                    }else if(i==0){
                        dp[i][j]=dp[i][j-1];
                    }else if(j==0){
                        dp[i][j]=dp[i-1][j];
                    }else{                       
                        dp[i][j]=dp[i-1][j]+dp[i][j-1];
                    }
                }else{
                    dp[i][j]=0;  
                    continue;
                }
            }            
        }  
        return dp[row-1][col-1];
    }
View Code

上面幾個都是連續型問題,樓梯台階連續,走的格子連續。

0-1背包問題 

背包的容量j因放入物品的重量w不同,變化非連續,但一般都有額外的空間(表)來存儲狀態信息。

遞歸遍歷解法:

        int []w={15,17,20,12,9,14};
    int []p={32,37,46,26,21,30};    
    public int solve(int i,int n,int j,int max){
        if(i<n){
            if(j>=w[i]){
                return Math.max(solve(i+1,n,j-w[i],max+p[i]),solve    (i+1,n,j,max));
            }else{
                return solve(i+1,n,j,max);
            }    
        }else
            return max;
    }    
View Code

 

dp[i][j]表示前i件物品恰放入一個容量為j的背包可獲得的最大價值
dp[i][j]=max{dp[i-1][j],dp[i-1][j-w[i]]+p[i]};
    public int Packet(int c){
        int []w={15,17,20,12,9,14};
       int []p={32,37,46,26,21,30};
        int packets=w.length;
        int [][]dp=new int[packets+1][c+1];
        for(int i=0;i<=packets;i++)
            for(int j=0;j<=c;j++){
                if(i==0||j==0)
                    dp[i][j]=0;
                else if(j>=w[i-1]){
                    dp[i][j]=Math.max(dp[i-1][j], dp[i-1][j-w[i-1]]+p[i-1]);
                }else
                    dp[i][j]=dp[i-1][j];
            }
        return dp[packets][c];
    }    

與貪心算法的每步取”最優“不同,動態規划存儲狀態信息,並有針對於狀態變量的狀態轉移策略。

 最大子序和

dp[i]表示截止到i的最大連續字串和,dp[0]=nums[0];
dp[i]=max{dp[i-1]+nums[i],nums[i]}

  

class Solution {
    public int maxSubArray(int[] nums){
        int len=nums.length;
        int[] dp=new int[len];
        for(int i=0;i<len;i++)
            if(i==0)
                dp[i]=nums[i];
            else
                dp[i]=Math.max(dp[i-1]+nums[i], nums[i]);
        Arrays.sort(dp);
        return dp[len-1];
    }
}

大佬寫的經空間優化后的代碼:

    public int maxSubArray(int[] nums) {
        int res = nums[0];
        int sum = 0;
        for (int num : nums) {
            if (sum > 0)
                sum += num;
            else
                sum = num;
            res = Math.max(res, sum);
        }
        return res;
    }

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM