动态规划

大多数动态规划问题都能被归类成两种类型:

自上而下:你从最顶端开始不断地分解问题,直到你看到问题已经分解到最小并已得到解决,之后只用返回保存的答案即可。这叫做记忆存储(*Memoization*),即递归。

自下而上:你可以直接开始解决较小的子问题,从而获得最好的解决方案。在此过程中,你需要保证在解决问题之前先解决子问题。这可以称为表格填充算法(*Tabulation,*table-filling algorithm**),即迭代

动态规划背后的基本思想非常简单。大致上,若要解一个给定问题,我们需要解其不同部分(即子问题),再合并子问题的解以得出原问题的解。 通常许多子问题非常相似,为此动态规划法试图仅仅解决每个子问题一次,从而减少计算量: 一旦某个给定子问题的解已经算出,则将其记忆化存储,以便下次需要同一个子问题解之时直接查表。 这种做法在重复子问题的数目关于输入的规模呈指数增长时特别有用。

动态规划问题满足三大重要性质

①最优子结构性质:如果问题的最优解所包含的子问题的解也是最优的,我们就称该问题具有最优子结构性质(即满足最优化原理)。最优子结构性质为动态规划算法解决问题提供了重要线索。

②子问题重叠性质:子问题重叠性质是指在用递归算法自顶向下对问题进行求解时,每次产生的子问题并不总是新问题,有些子问题会被重复计算多次。动态规划算法正是利用了这种子问题的重叠性质,对每一个子问题只计算一次,然后将其计算结果保存在一个表格中,当再次需要计算已经计算过的子问题时,只是在表格中简单地查看一下结果,从而获得较高的效率。

无后效性:将各阶段按照一定的次序排列好之后,对于某个给定的阶段状态,它以前各阶段的状态无法直接影响它未来的决策,而只能通过当前的这个状态。换句话说,每个状态都是过去历史的一个完整总结。这就是无后向性,又称为无后效性。

/*
题目描述
给你一根长度为n的绳子,请把绳子剪成整数长的m段(m、n都是整数,n>1并且m>1),
每段绳子的长度记为k[0],k[1],...,k[m]。请问k[0]xk[1]x...xk[m]可能的最大乘积是多少?
例如,当绳子的长度是8时,我们把它剪成长度分别为2、3、3的三段,此时得到的最大乘积是18。
*/

import java.util.*;
public class Solution {
    public int cutRope(int n) {
        
        //首先对n 进行判断,小于4为一种情况:
        if(n==2)
            return 1;
        else
            if(n==3)
                return 2;
        //对于大于等于4的情况,首先定义dp 数组,最优子结构,从下到上,迭代求解
        int[] dp= new int[n+1];
        dp[1]=1;
        dp[2]=2;
        dp[3]=3;
        
        //对于每个长度下,都要获取每一个长度下的最大值,所以需要i从4递增至n,
        //同时对于每一个长度i,又可以分为两部分j,i-j(状态转移方程),j从1递增到2/n,
        for(int i=4;i<=n;i++)
        {
            int res=0;
            for(int j=1;j<=i/2;j++)
            {
                res=Math.max(res,dp[j]*dp[i-j]);
            }
            dp[i]=res;
        }
        return dp[n];
                
    }
}

import java.util.*;

public class Solution {
    public int minimumTotal(ArrayList<ArrayList<Integer>> triangle) {
        int m=triangle.size();
        int res=0;
        for(int i=m-2;i>=0;i--){
            for(int j=0;j<triangle.get(i).size();j++)
            {
                triangle.get(i).set(j,
            Math.min(
            triangle.get(i
+1).get(j)+triangle.get(i).get(j),
            triangle.get(i+1).get(j+1)+triangle.get(i).get(j))); } } return triangle.get(0).get(0); } }

 

import java.util.*;
public class Solution {
    public int uniquePaths (int m, int n) {
        // write code here
        if(m==1 || n==1)
            return 1;
         //dp[i][j]表示机器人从点(0,0)出发到点(i,j)的不同路径数目
        int dp[][]=new int[m][n];
        dp[0][0]=0;
         /*考虑边缘情况*/
        //n = 1的情况,只有一条直线路径
        for(int i=1;i<m;i++)
            dp[i][0]=1;
        //m = 1的情况,只有一条直线路径
        for(int j=1;j<n;j++)
            dp[0][j]=1;
        //动态规划
        for(int i=1;i<m;i++)
            for(int j=1;j<n;j++)
            {
                //到达点(i,j)有两种路径:
                //(1)机器人从点(i-1,j)往下移动 (2)机器人从点(i,j-1)往右移动 
                dp[i][j]=dp[i-1][j]+dp[i][j-1];
            }
        return dp[m-1][n-1];
        
    }
}

 

import java.util.*;


public class Solution {
    public int uniquePathsWithObstacles (int[][] obstacleGrid) {
        // write code here
        int row=obstacleGrid.length;
        int col=obstacleGrid[0].length;
        int dp[][]=new int[row][col];

        //设置第一行,注意有障碍时,后面的都要为0
        for(int i=0;i<row;i++)
        {
            if(obstacleGrid[i][0]==1)
                dp[i][0]=0;
            else
                dp[i][0]=i==0?1:dp[i-1][0];
        }
        //设置第一列,注意有障碍时,后面的都要为0
        for(int j=0;j<col;j++)
        {
            if(obstacleGrid[0][j]==1)
                dp[0][j]=0;
            else
                dp[0][j]=j==0?1:dp[0][j-1];
        }
        for(int i=1;i<row;i++)
        {
            for(int j=1;j<col;j++)
            {
                if(obstacleGrid[i][j]==1)
                    dp[i][j]=0;
                else
                {
                    dp[i][j]=dp[i-1][j]+dp[i][j-1];
                }
            }
        }
        
        return dp[row-1][col-1];
        
    }
}

 

import java.util.*;


public class Solution {
    /**
     * 
     * @param grid int整型二维数组 
     * @return int整型
     */
    public int minPathSum (int[][] grid) {
        // write code here
        int m=grid.length;
        int n=grid[0].length;
        
        int [][]dp =new int[m][n];
        dp[0][0]=grid[0][0];
        for(int i=1;i<m;i++)
        {
            dp[i][0]=dp[i-1][0]+grid[i][0];
        }
        
        for(int j=1;j<n;j++)
        {
            dp[0][j]=dp[0][j-1]+grid[0][j];
        }
        
        
        for(int i=1;i<m;i++)
        {
            for(int j=1;j<n;j++)
            {
                dp[i][j]=grid[i][j]+Math.min(dp[i][j-1],dp[i-1][j]);
            }
        }
            return dp[m-1][n-1];
    }
}
import java.util.*;
public class Main{
    public static void main(String [] args){
        Scanner sc=new Scanner(System.in);
        while(sc.hasNextInt())//是由于有很多个测试用例
        {
            int n=sc.nextInt();
            int [] nums=new int[n];
            for(int i=0;i<n;i++){
                nums[i]=sc.nextInt();
            }
             System.out.println(res(nums,n));
        }

    }
    public static int res(int [] nums,int n)
    {
        int max=1;
        int [] dp=new int[n];
        for(int i=0;i<n;i++)
        {
            dp[i]=1;
            for(int j=0;j<i;j++)
            {
                if(nums[j]<nums[i])
                {
                    dp[i]=Math.max(dp[i],dp[j]+1);
                }
            }
            max=Math.max(max,dp[i]);
            
        }
        return max;
    }
}
原文地址:https://www.cnblogs.com/lemonzhang/p/13026820.html