动态规划例题总结


一、01背包问题

题目描述:有n个重量和价值分别为wi,vi的物品,从这些物品中挑选出总重量不超过W的物品,求所有挑选方案中价值总和的最大值。
       1<=n<=100
       1<=wi,vi<=100
       1<=w<=10000
样例:
输入
       n=4
       (w,v)={(2,3),(1,2),(3,4),(2,2)}
       W=5
输出
       7(选择0,1,3号物品)
因为对每个物品只有选和不选两种情况,所以称这个问题为01背包

解法一:
思路分析:这一种解法采用的是深度优先搜索,遍历每一种情况,选取其中的最大值。深搜的情况分为两种,一种是选当前位置,一种是不选当前位置。解法首先调用自定义的函数,然后分为两种情况,如果是不选的话,最大重量W不变,可选位置current+1。如果选的话,最大重量变为W - w[current],可选位置current+1。继续递归。然后每次递归过程中,都要选取两种情况中最大的。最后要确定出口,输出结果。

    static int W=5;                                             //最大重量
    static int n=4;                                             //物品数
    static int[] w= {2,1,3,2};                                  //每个物体的重量
    static int[] v= {3,2,4,2};                                  //每个物体的价值
    
    public static void main(String[] args) {
        int x=f(W,0);                                           //调用自定义函数
        System.out.println(x);                                  //输出结果
    }
    private static int f(int W, int current) {
        if (W <= 0) {                                                   //出口判断
            return 0;
        }
        if (current == n) {                                             //出口判断
            return 0;
        }
        int value1 = f(W, current + 1);                                //不选的情况
        if (W >= w[current]) {
            int value2 = v[current] + f(W - w[current], current + 1);   //选当前的情况
            int temp = Math.max(value1, value2);                        //取两者较大值
            return temp;
        }
        return value1;
    }

解法二:
思路分析:其实第二种解法只是第一种解法的简单优化,所以只标注一些优化后的注释。优化在于每次递归其实都会有重复的地方,故创建一个记忆数组,用于记录之前递归中的值。记忆数组是一个二维数组,arr【当前位置current】【剩余最大乘重量】。记忆数组要先初始化,将所有位置置为-1。在方法调用中,如果值大于0,则表明之前已经调用过,可以直接获取值并返回。

    static int W=5;                                             //最大重量
    static int n=4;                                             //物品数
    static int[] w= {2,1,3,2};                                  //每个物体的重量
    static int[] v= {3,2,4,2};                                  //每个物体的价值
    static int[][] arr=new int[n][W+1];                         //记忆数组                  
    
    public static void main(String[] args) {
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < W + 1; j++) {
                arr[i][j] = -1;                                 //初始化记忆数组
            }
        }
        int x = f(W, 0);                                        // 调用自定义函数
        System.out.println(x);                                  // 输出结果
    }
    //记忆型递归
    private static int f(int W, int current) {
        if (W <= 0) {
            return 0;
        }
        if (current == n) {
            return 0;
        }
        if (arr[current][W] >= 0) {                                     //如果记忆数组中含有值
            return arr[current][W];                                     //取值,直接返回
        }
        int value1 = f(W, current + 1);
        int value2 = 0;
        if (W >= w[current]) {
            value2 = v[current] + f(W - w[current], current + 1);
        }
        int judge = Math.max(value1, value2);                           //选取两种情况中的较大值
        arr[current][W] = judge;                                        //存入记忆数组中
        return judge;
    }

解法三:
思路分析:这题采用的是dp解法,博主可能无法完全用语言描述清楚dp解法,故需先自己了解原理。这个题采用了dp解法,需要使用到Excel表格,第一行代表最大载重量,第一列代表每一种物品。需要首先初始化表格中的第二行,也就是f方法中的第一个for循环。然后遍历每一行,也是有两种情况,第一种情况是不取,那就直接选取上一行的较大值。第二种情况是取,然后就用i号物品的价值+上一行中对应的最大载重量。其实就是填表格,直到填完最后一个格子,最后一个格子即为最大值。(博主已经尽力描述了,如果没有让你明白,深感抱歉。)

在这里插入图片描述

static int W=5;                                             //最大重量
    static int n=4;                                             //物品数
    static int[] w= {2,1,3,2};                                  //每个物体的重量
    static int[] v= {3,2,4,2};                                  //每个物体的价值
    static int[][] arr=new int[n][W+1];                         //记忆数组                  
    
    public static void main(String[] args) {
        int x = f(W, 0);                                        // 调用自定义函数
        System.out.println(x);                                  // 输出结果
}
    //dp解法
    private static int f(int W, int current) {
        for (int i = 0; i < W + 1; i++) {
            if (i >= w[0]) {
                arr[0][i] = v[0];                               //初始化第一行
            }   
        }
        for (int i = 1; i < n; i++) {
            for (int j = 0; j < W + 1; j++) {
                if (j >= w[i]) {
                    arr[i][j] = Math.max(v[i] + arr[i - 1][j - w[i]], arr[i - 1][j]);   //选取两种情况中的较大值
                } else {
                    arr[i][j] = arr[i - 1][j];                      //只有一种情况,直接存入
                }
            }
        }
        return arr[n - 1][W];
    }


二、数字三角形问题

**题目描述:在数字三角形中寻找一条从顶部到底边的路径,使得路径上所经过的数字之和最大。路径上的每一步都只能往左下或右下走。只需要求出最大和即可,不比给出路径。三角形的行数大于1小于等于100,数字为0~99
       1<=n<=100
       1<=wi,vi<=100
       1<=w<=10000
样例:
输入

在这里插入图片描述

输出
30

解法一:
思路分析:这题也是采用深搜解法。。首先调用自定义的函数f1,首先获取三角形的最大行数maxLength。如果maxLength==行数i,则直接返回triangle[i][j]。否则继续递归调用,用(i,j)上的值加上下一行的最大值,并返回。最后输出结果

public static void main(String[] args) {
        int[][] triangle= {
                {7},
                {3,8},
                {8,1,0},
                {2,7,4,4},
                {4,5,2,6,5}
        };
        int temp=f2(triangle,0,0);                              //调用自定义函数
        System.out.println(temp);
    }
    //dfs解法
    public static int f1(int[][] triangle,int i,int j) {
        int maxLength=triangle.length;                          //三角形的行数
        if(i==maxLength-1) {
            return triangle[i][j];                              //到达最后一行,直接返回
        }else {
            return triangle[i][j]+Math.max(f1(triangle,i+1,j), f1(triangle,i+1,j+1));   //(i,j)位上的数字加上下一行的较大值
        }
    }

解法二:
思路分析:这题采用的是dp解法。首先还是相当于建表格。首先初始化三角形的最后一行,将他存入新的二维数组中。然后从倒数第二行开始往上填,每次都选取下一个中的较大值。直到填完第一行。二维数组中的[0][0]号位即为结果。

在这里插入图片描述

public static void main(String[] args) {
        int[][] triangle= {
                {7},
                {3,8},
                {8,1,0},
                {2,7,4,4},
                {4,5,2,6,5}
        };
        int temp=f2(triangle);                              //调用自定义函数
        System.out.println(temp);
    }
    //dp解法
        public static int f2(int[][] triangle) {
            int maxLenth=triangle.length;                       //获取最大行数
            int[][] arr=new int[maxLenth][maxLenth];            //新建一个二维数组
            for(int q=arr.length-1;q>=0;q--) {
                arr[maxLenth-1][q]=triangle[maxLenth-1][q];     //初始化二维数组的最后一行
            }
            for(int k=arr.length-2;k>=0;k--) {
                for(int c=0;c<=k;c++) {
                    arr[k][c]=triangle[k][c]+Math.max(arr[k+1][c], arr[k+1][c+1]);  //从下往上填,选择下一行中较大的数字+(k,c)号位上的值
                }
            }
            return arr[0][0];                                   //返回结果
        }


三、完全背包问题

**题目描述:有n个重量和价值分别为wi,vi的物品,从这些物品中挑选出总重量不超过W的物品,求所有挑选方案中价值总和的最大值。
样例:
      1<=n<=100
      1<=wi,vi<=100
      1<=W<=10000
输入
n=5
(w,v)={(2,3),(1,2),(3,4),(2,2)}
输出
10

解法一:
思路分析:这题读者可以将他与01背包类比。这道题的和第一题的不同之处在于没有限定一个物品有多少个,因此可以选多个同一种物品。分析之后,和第一题一样,存在两种情况。第一种情况是不取,那就直接取arr[i-1][j]中的值。第二种情况是取,那么就是v[i]+arr[i][j-w[i]]。也是和前面的题一样,填表格。可以先将第一行和第一列初始化,然后逐个空填满表格,最后一个空即为最大值。

在这里插入图片描述

public static void main(String[] args) {
        int answer=f();
        System.out.println(answer);
    }
    static int W=2;                                             //最大重量
    static int n=4;                                             //物品数
    static int[] w= {2,1,3,2};                                  //每个物体的重量
    static int[] v= {3,2,4,2};                                  //每个物体的价值
    public static int f() {
        int[][] arr=new int[n][W+1];
        for(int i=0;i=w[i]) {                                   //如果该物品重量小于最大载重量
                int temp1=arr[i-1][j];                          //第一种情况,不取该物品                       
                int temp2=v[i]+arr[i][j-w[i]];                  //第二种情况,不取该物品
                arr[i][j]=Math.max(temp1, temp2);
                }else {                                         //否则
                    arr[i][j]=arr[i-1][j];                      //直接是不取该物品
                }
            }
        }
        return arr[n-1][W];
    }


四、最长上升子序列的问题

**题目描述:最长递增子序列的长度
输入
4 2 3 1 5 6
输出
3(因为2 3 5组成了最长递增子序列)

解法一:
思路分析:第一种解法采用的简单的暴力解法。在函数中首先初始化最大长度max=0。然后两层循环遍历每一个字符,如果后面的字符比前面的字符打,就长度+1。

public static void main(String[] args) {
        int[] arr = { 4, 2, 3, 1, 5 };
        System.out.println(f(arr));                         // 调用自定义的函数
    }

    private static int f(int[] arr) {
        int max = 0;                                        //记录最大长度
        for (int i = 0; i < arr.length; i++) {
            int p = i;                                      //指针,初始化为i号字符
            int cnt = 1;                                    //初始化长度为1,因为i号位就是第一个字符
            for (int j = i + 1; j < arr.length; j++) {
                if (arr[j] > arr[p]) {                              
                    cnt++;                                  //长度加一
                    p = j;                                  //p变更为j号位
                }
            }
            if (cnt > max) {
                max = cnt;                                  //变更最大长度
            }
        }
        return max;
    }

解法二:
思路分析:第二种解法是dp解法。这里的想法是首先初始化一个长度数字,将每个位置置为1,也就是最开始每个数字都是独立的,所以单个数字的长度为1。然后遍历每一个数字,第二层循环遍历i号位之前的每一个数字,如果i号位上的数字大于j号位上的数字,则说明构成递增序列。因此就把长度数组j号位上的值+1和长度数组上i号位已经有的值比较大小,取较大值。最后遍历长度数组,输出结果。

在这里插入图片描述

//dp解法
    public static void main(String[] args) {
        int[] arr = { 4, 2, 3, 1, 5 };
        f(arr);                         // 调用自定义的函数
    }
    public static void f(int[] arr) {
        int[] list=new int[arr.length];                     //新建一个长度数组
        for(int i=0;i=0;j--) {                       //遍历i号位数字之前的每个数字
                if(arr[i]>arr[j]) {                         //如果i号位的数字大于j号位的数字
                    list[i]=Math.max(list[j]+1, list[i]);   //j号位数字的最大长度+1和i号位上的最大长度比较,取较大值
                }
            }
        }
        int max=0;
        for(int i=0;imax) {
                max=list[i];                                //遍历长度数组,获取最大长度
            }
        }
        System.out.println(max);
        
    }

你可能感兴趣的:(动态规划例题总结)