动态规划算法题

1.HJ32 密码截取

2.HJ24 合唱队

3.HJ52 计算字符串的编辑距离

4.HJ61 放苹果

5.HJ75 公共子串计算

6.HJ41 称砝码

3.HJ52 计算字符串的编辑距离

描述

Levenshtein 距离,又称编辑距离,指的是两个字符串之间,由一个转换成另一个所需的最少编辑操作次数。许可的编辑操作包括将一个字符替换成另一个字符,插入一个字符,删除一个字符。编辑距离的算法是首先由俄国科学家 Levenshtein 提出的,故又叫 Levenshtein Distance 。

例如:

字符串A: abcdefg

字符串B: abcdef

通过增加或是删掉字符 ”g” 的方式达到目的。这两种方案都需要一次操作。把这个操作所需要的次数定义为两个字符串的距离。

要求:

给定任意两个字符串,写出一个算法计算它们的编辑距离。

数据范围:给定的字符串长度满足 1 \le len(str) \le 1000 \1≤len(str)≤1000 

输入描述:

每组用例一共2行,为输入的两个字符串

输出描述:

每组用例输出一行,代表字符串的距离

import java.util.*;

public class Main
{
    public static void main(String[] args)
    {
        Scanner sc = new Scanner(System.in);
        while(sc.hasNext())
        {
            String s1 = sc.nextLine();
            String s2 = sc.nextLine();
            System.out.println(distance(s1,s2));  
        }
    }
    
    private static int distance(String s1,String s2)
    {
        int m = s1.length();
        int n = s2.length();
        //定义dp数组
        int[][] dp = new int[m+1][n+1];
        //状态初始化
        for(int i = 1;i <= m; i++)
        {
            dp[i][0] = i;
        }
        //状态转移
        for(int i = 1;i <=m;i++)
        {
            for(int j = 1;j <= n;j++)
            {
                if(s1.charAt(i-1) == s2.charAt(j-1))
                {
                    dp[i][j] = dp[i-1][j-1];
                }
                /*如果不相等,要么B字符串插入A中i位置对应字符即dp[i][j]=dp[i-1][j]+1
                要么A字符串插入B中j位置对应字符即dp[i][j]=dp[i][j-1]+1,要么s1字符串
                i位置字符被s2字符串j位置字符替换,即dp[i][j]=dp[i-1][j-1]+1
                */
                else
                {
                 dp[i][j]=Math.min(
                 Math.min(dp[i-1][j],dp[i][j-1]),dp[i-1][j-1])+1;
                }
            }
        }
        return dp[m][n];
    }
    
} 

2.HJ24 合唱队

描述

N 位同学站成一排,音乐老师要请最少的同学出列,使得剩下的 K 位同学排成合唱队形。

设KK位同学从左到右依次编号为 1,2…,K ,他们的身高分别为T_1,T_2,…,T_KT1​,T2​,…,TK​ ,若存在i(1\leq i\leq K)i(1≤i≤K) 使得T_1<T_2<......<T_{i-1}<T_iT1​<T2​<......<Ti−1​<Ti​ 且 T_i>T_{i+1}>......>T_KTi​>Ti+1​>......>TK​,则称这KK名同学排成了合唱队形。

通俗来说,能找到一个同学,他的两边的同学身高都依次严格降低的队形就是合唱队形。

例子:

123 124 125 123 121 是一个合唱队形

123 123 124 122不是合唱队形,因为前两名同学身高相等,不符合要求

123 122 121 122不是合唱队形,因为找不到一个同学,他的两侧同学身高递减。

你的任务是,已知所有N位同学的身高,计算最少需要几位同学出列,可以使得剩下的同学排成合唱队形。

注意:不允许改变队列元素的先后顺序  不要求最高同学左右人数必须相等

数据范围: 1 \le n \le 3000 \1≤n≤3000 

输入描述:

用例两行数据,第一行是同学的总数 N ,第二行是 N 位同学的身高,以空格隔开

输出描述:

最少需要几位同学出列

输入:8

186 186 150 200 160 130 197 200

输出:4

说明:

由于不允许改变队列元素的先后顺序,所以最终剩下的队列应该为186 200 160 130或150 200 160 130    

题目分析:站成一排,从左到右找到中间人,从左到中间人是递增,从中间人到右是递减,所以从左到中间人找最长递增子序列的长度,找从中间人到右的递减子序列的长度。首先这个考察的是最长公共子序列长度怎么求取,变化是两个递增子序列的长度,一个是在从左到右到i位置是递增,还有一个从右到左,从n到i最长的递增子序列,这两个最长的递增子序列长度相加减去1是合唱队队形,遍历全部取最大值。

思路分析:

  1. 数据输入,有两部分数据,第一部分是合唱队的个数,第二个是每个数据
  2. 分为两部分的动态数组来记录左边的递增最长子序列
  3. 左边的,定义一个数组记录每个位置的最长递增序列
  4. 用双层for循环进行判断,第一层是循环数组是把每个位置初始化1,因为自己的一个数本身就是1个长度,第二层进行判断前i个数据中,比i数据小的话,则取当前数组的最长子序列长度+1,并取所有比i位置小的数据的最长递增子序列长度的最大值。
  5. 右边的,定义一个数组记录每个位置的最长递增子序列
  6. 用双层for循环进行判断,第一层是循环数组是把每个位置初始化1,因为自己的一个数本身就是1个长度,第二层进行判断前i个数据中,比i数据小的话,则取当前数组的最长子序列长度+1,并取所有比i位置小的数据的最长递增子序列长度的最大值。
  7. 再把i位置左边最长递增子序列长度和右边最长递增子序列长度相加减去1,作为该合唱队的个数,求取最大的合唱队长度。
  8. 最后用n-最长的递增子序列,就是最少需要取出多少人。

实际操作:

  1. 数据输入:第一个是int类型,第二个需要定义一个数组进行判断,进行for循环进行取数。
  2. 定义一维数组 left[i],记录从左到i位置最长的子序列长度。
  3. 定义一维数组right[i],记录从n-1到i位置最长的递增子序列
  4. 初始化数组,left[0] = 1; right[n-1] = 1;
  5. 遍历最左侧最长递增子序列,两层循环,第一层是循环数组是把每个位置初始化1,因为自己的一个数本身就是1个长度,第二层进行判断前i个数据中,比i数据小的话,则取当前数组的最长子序列长度+1,并取所有比i位置小的数据的最长递增子序列长度的最大值。
  6. 遍历最右边最长递增子序列,第一层是循环数组是把每个位置初始化1,因为自己的一个数本身就是1个长度,第二层进行判断前i个数据中,比i数据小的话,则取当前数组的最长子序列长度+1,并取所有比i位置小的数据的最长递增子序列长度的最大值
  7. 再把i位置左边最长递增子序列长度和右边最长递增子序列长度相加减去1,作为该合唱队的个数,求取最大的合唱队长度。
  8. 最后用n-最长的递增子序列,就是最少需要取出多少人。
import java.util.Scanner;
/**
问题分析:站成一排,从左到右找到中间人,从左到中间人是递增,从中间人到右是递减,
所以从左到中间人找最长递增子序列的长度,找从中间人到右的递减子序列的长度。

思路分析:
1.数据输入:输入两行,第一行是数字的个数,用int接收,第二行是所有数据,用数组接收。
2.
*/
public class Main
{
    public static void main(String[] args)
    {
        Scanner sc = new Scanner(System.in);
        while(sc.hasNext())
        {
           int n = sc.nextInt();
           int[] arr = new int[n];
           for (int i = 0; i < n; i++) 
            {
                arr[i] = sc.nextInt();
            }
            int[] left = new int[n];
            int[] right = new int[n];
            left[0] = 1;
            right[n-1] = 1;
            //计算每个位置左侧的最长递增
            for (int i=0; i < n; i++)
            {
                left[i] = 1;
                for ( int j = 0; j < i;j++)
                {
                    if (arr[i] > arr[j])
                    {
                        left[i] = Math.max(left[j]+1,left[i]);
                    }
                }
            }
            
            //计算每个位置右侧的最长递减
            for (int i = n-1; i >= 0;i--)
            {
                right[i] = 1;
                for (int j = n-1;j > i;j--)
                {
                    if (arr[i] > arr[j])
                    {
                        right[i] = Math.max(right[i],right[j]+1);
                    }
                }
            }
            //记录每个位置的值
            int[] result = new int[n];
            for (int i = 0; i < n;i++)
            {
                result[i] = left[i] + right[i] - 1;
            }
            //找到最大的满足要求的值
            int max = 1;
            for (int i = 0;i < n;i++)
            {
                max = Math.max(result[i],max);
            }
            System.out.println(n - max);
        } 
    }
}

8.HJ16 购物单

描述

王强决定把年终奖用于购物,他把想买的物品分为两类:主件与附件,附件是从属于某个主件的,下表就是一些主件与附件的例子:

主件附件
电脑打印机,扫描仪
书柜图书
书桌台灯,文具
工作椅

如果要买归类为附件的物品,必须先买该附件所属的主件,且每件物品只能购买一次。

每个主件可以有 0 个、 1 个或 2 个附件。附件不再有从属于自己的附件。

王强查到了每件物品的价格(都是 10 元的整数倍),而他只有 N 元的预算。除此之外,他给每件物品规定了一个重要度,用整数 1 5 表示。他希望在花费不超过 N 元的前提下,使自己的满意度达到最大。

满意度是指所购买的每件物品的价格与重要度的乘积的总和,假设设第ii件物品的价格为v[i]v[i],重要度为w[i]w[i],共选中了kk件物品,编号依次为j_1,j_2,...,j_kj1​,j2​,...,jk​,则满意度为:v[j_1]*w[j_1]+v[j_2]*w[j_2]+ … +v[j_k]*w[j_k]v[j1​]∗w[j1​]+v[j2​]∗w[j2​]+…+v[jk​]∗w[jk​]。(其中 * 为乘号)

请你帮助王强计算可获得的最大的满意度。

输入描述:

输入的第 1 行,为两个正整数N,m,用一个空格隔开:

(其中 N ( N<32000 )表示总钱数, m (m <60 )为可购买的物品的个数。)

从第 2 行到第 m+1 行,第 j 行给出了编号为 j-1 的物品的基本数据,每行有 3 个非负整数 v p q

(其中 v 表示该物品的价格( v<10000 ), p 表示该物品的重要度( 1 5 ), q 表示该物品是主件还是附件。如果 q=0 ,表示该物品为主件,如果 q>0 ,表示该物品为附件, q 是所属主件的编号)

输出描述:

 输出一个正整数,为张强可以获得的最大的满意度。

示例1

输入:

1000 5
800 2 0
400 5 1
300 5 1
400 3 0
500 2 0

输出:2200

1)题目分析:

  1. 这个题目是0和1背包问题,可以用动态规划,背包问题是定义,变量是物品和背包剩余容量

  2. dp[i][j]表示前i个物品,背包容量为j时的最大价值,物品的重量用w[i]表示,

  3. 物品的价值用v[i]表示。

  4. 分为两种状态:分为放或者不放两种选择

  5. 放得话就是dp[i][j] = dp[i-1][j-w[i]] + v[i]

  6. 不放得话就是dp[i][j] = dp[i-1][j]

  7. 最后取得dp[i][j] = Math.max(dp[i-1][j],dp[i-1][j-w[i]] + v[i])

2)思路分析:

  1. 本题在此基础上有一些条件,就是有附件的必须要有主件,
  2. 首先分两种情况,放或者不放
  3. 不放的时候一种方法,
  4. 当放的时候,有四种方法,
  5. 第一只放主件;
  6. 第二只放1个主件和附件1
  7. 第三十放1个主件和附件2
  8. 第四是放1个主件、附件1和附件2
  9. 总共有5种方法,这五种方法每次取最大值

3)实际操作

  1. 首先这道题的输入参数比较多,自己需要定义数据结构体,该数据结构比较复杂。我们分析样例,第一行有两个整型的数据,第一个是用户手上的钱,第二个是物品的个数,剩余的几行都是每个物品的三个属性,第一个是物品的价格,第二个是物品的重要度,第三个是标识,代表的是是否是主件,如果是主件则为0,如果是附件数据表示的是则为第几个主件的附件。那么可以定义一维数组,数组的长度为物品的个数,数组的每个元素是一个物品对象,里面包含了四个属性,该物品的价格v,物品的重要度,物品不是主件,附件1初始化-1,附件2初始化-2。
  2. 定义输入流对象,接收两个整型的数据,价格和物品个数
  3. 定义一维数组,数组的长度是物品的个数
  4. 遍历一维数组,给每一个值创建一个物品对象 new Goods
  5. 遍历输入的每一行,赋值给每一个对象
  6. 遍历第一行的每个数据,第一个数据赋值给goods[i].v = v
  7. 遍历第一行的每个数据,第二个数据赋值给goods[i].p = p*v
  8. 遍历第一行的每个数据,第三个数据进行判断,如果为0,则把物品为否的主件改为是 goods[i].main = true;如果不是,则为附件,如果附件goods[i].a1 == -1,则说明是第一个附件,把当前第几个物品赋值给a1,如果附件goods[i].a1 != -1,goods[i].a2 != -1是,则说明是第二个附件,把当前第几个物品赋值给a2。  这样就把输入的数据处理完成了。
  9. 输入的数据流处理完成后,就是处理业务规则。
  10. 首先是变量是物件的个数和钱的数量。所以可以定义dp[i][j]表示前i件物品,手上持有j钱时最大满意度。
  11. 用for循环进行双层遍历,并进行判断。
  12. 目前,有i件物品,手上持有j钱时,当不取i物品时,用户最大满意度为dp[i][j] = dp[i-1][ j ]
  13. 目前,有i件物品,手上持有j钱时,取i物品时,判断该物品是不是附件,if(!goods[i-1].main),如果是继续循环,如果不是,
  14. 则判断为主件,手持的钱是否大于买i-1的东西,if(j >= goods[i - 1].v),,则拿当前的i-1物品,dp[i][j] = Math.max(dp[i][j],dp[i-1][j-goods[i-1].v]+goods[i-1].p);
  15. 则判断是否为附件1if(goods[i-1].a1 == -1),手持的钱是否大于买i-1的东西和附件1的钱,如果是的话,dp[i][j] = Math.max(dp[i][j],dp[i-1][j-goods[i-1].v-goods[goods[i-1].a1].v]+goods[i-1].p+goods[goods[i-1].a1].p);
  16. 则判断是否为附件2if(goods[i-1].a2 == -1),手持的钱是否大于买i-1的东西和附件2的钱,如果是的话,dp[i][j] = Math.max(dp[i][j],dp[i-1][j-goods[i-1].v-goods[goods[i-1].a2].v]+goods[i-1].p+goods[goods[i-1].a2].p);
  17. 剩下的是拿附件1和附件2的东西,最后一种方法。
     
import java.util.Scanner;
/**
题目分析:
这个题目是0和1背包问题,可以用动态规划,背包问题是定义,变量是物品和背包剩余容量
dp[i][j]表示前i个物品,背包容量为j时的最大价值,物品的重量用w[i]表示,
物品的价值用v[i]表示。
分为两种状态:分为放或者不放两种选择
放得话就是dp[i][j] = dp[i-1][j-w[i]] + v[i]
不放得话就是dp[i][j] = dp[i-1][j]
最后取得dp[i][j] = Math.max(dp[i-1][j],dp[i-1][j-w[i]] + v[i])

本题在此基础上有一些条件,就是有附件的必须要有主件,
首先分两种情况,放或者不放
不放的时候一种方法,
当放的时候,有四种方法,
第一只放主件;
第二只放1个主件和附件1
第三十放1个主件和附件2
第四是放1个主件、附件1和附件2
总共有5种方法,这五种方法每次取最大值

实际操作:
首先定义dp[i][j]表示前i件物品,手上持有金额为j的时候用户最大满意度,
物品的金额为w[i],物品的满意度v[i],附件设为下标为a1、a2
递推公式:
不放:dp[i][j] = dp[i-1][j]
第一只放主件:dp[i-1][j-w[i]] + v[i]
第二只放1个主件和附件1:dp[i-1][j-w[i]-w[a1]] + v[i]+v[a1]
第三只放1个主件和附件2:dp[i-1][j-w[i]-w[a2]] + v[i]+v[a2]
第四是只放1个主件、附件1和附件2:dp[i-1][j-w[i]-w[a1]-w[a2]] + v[i]+v[a2]+v[a1]
最后取得dp[i][j] =
Math.max
(
Math.max(dp[i-1][j],dp[i-1][j-w[i]] + v[i]),
Math.max(dp[i-1][j-w[i]-w[a1]] + v[i]+v[a1],dp[i-1][j-w[i]] + v[i]),
Math.max(dp[i-1][j-w[i]-w[a2]] + v[i]+v[a2],dp[i-1][j-w[i]-w[a1]-w[a2]] + v[i]+v[a2]+v[a1])
)
初始化值:
*/
public class Main
{
    public static void main(String[] args)
    {
        Scanner scanner = new Scanner(System.in);
        while(scanner.hasNext())
        {
            int  n = scanner.nextInt();
            int  m = scanner.nextInt();
            
            Goods[] goods = new Goods[n];
            for(int i=0; i < n; i++)
            {
                goods[i] = new Goods();
            }
            
            for (int i=0; i < m; i++)
            {
                int v = scanner.nextInt();
                int p = scanner.nextInt();
                int q = scanner.nextInt();
                goods[i].v = v;
                goods[i].p = p*v;
                if(q==0)
                {
                    goods[i].main = true;
                }
                else if(goods[q-1].a1 == -1)
                {
                    goods[q-1].a1 = i;
                }
                else
                {
                    goods[q-1].a2 = i;
                }
                
            }
            
            int[][] dp = new int[m+1][n+1];
            for(int i = 1; i <= m; i++)
            {
                for(int j = 1; j <= n; j++)
                {
                    dp[i][j] = dp [i-1][j];
                    if(!goods[i-1].main)
                    {
                        continue;
                    }
                    if(j >= goods[i-1].v)
                    {
                        dp[i][j] = Math.max(dp[i][j],dp[i-1][j-goods[i-1].v]+goods[i-1].p);
                    }
                    if( goods[i-1].a1 != -1 && j >= goods[i-1].v +goods[goods[i-1].a1].v)   
                    {
                       dp[i][j] = Math.max(dp[i][j],dp[i-1][j-goods[i-1].v-goods[goods[i-1].a1].v]+goods[i-1].p+goods[goods[i-1].a1].p);
                    }
                     if( goods[i-1].a2 != -1 && j >= goods[i-1].v +goods[goods[i-1].a2].v)   
                    {
                       dp[i][j] = Math.max(dp[i][j],dp[i-1][j-goods[i-1].v-goods[goods[i-1].a2].v]+goods[i-1].p+goods[goods[i-1].a2].p);
                    } 
                     if( goods[i-1].a2 != -1 &&goods[i-1].a1 != -1 && j >= goods[i-1].v +goods[goods[i-1].a2].v+goods[goods[i-1].a1].v)   
                    {
                       dp[i][j] = Math.max(dp[i][j],dp[i-1][j-goods[i-1].v-goods[goods[i-1].a2].v-goods[goods[i-1].a1].v]+goods[i-1].p+goods[goods[i-1].a2].p+goods[goods[i-1].a1].p);
                    } 
                }
            }
             System.out.println(dp[m][n]);  
        }
    }
                    
}

class Goods
    {
        int v;
        int p;
        boolean main = false;
        
        int a1 = -1;
        int a2 = -1;
    }

7.走方格数

描述

请计算n*m的棋盘格子(n为横向的格子数,m为竖向的格子数)从棋盘左上角出发沿着边缘线从左上角走到右下角,总共有多少种走法,要求不能走回头路,即:只能往右和往下走,不能往左和往上走。

注:沿棋盘格之间的边缘线行走

数据范围: 1 \le n,m \le 8 \1≤n,m≤8 

输入描述:

输入两个正整数n和m,用空格隔开。(1≤n,m≤8)

输出描述:

输出一行结果

思路分析:

  1. 这个是两种状态,向左或者向右,那么可以递归,会重复用到之前的值,所以用动态递归
  2. 定义dp[i][j]]是从i,j位置走到右下角有dp[i][j]种算法
  3. 递推公式dp[i][j]=dp[i-1][j]+dp[i][j-1]
  4. 初始化值,当只有一行或者一列时,都只有一种方法dp[i][0]=1;,dp[0][j]=1;
public class Main
{
    public static void main(String[] args)
    {
        Scanner  scanner = new Scanner(System.in);
        while(scanner.hasNext())
        {
            int n = scanner.nextInt();
            int m = scanner.nextInt();
            System.out.println(scheme(n, m));
        }
    }
        
         //定义函数,参数是方格的横和列的位置,返回值是走法数
    public  static int scheme(int n, int m)
        {
                if(n<1 ||  m> 8)
                { return 0;}//定义一个二维数据dp[i][j]是从i,j这个位置走到右下角的方法数
            int[][] dp = new int[n+1][m+1];
                for(int i=0;i<n+1;i++)
                 {   
                     dp[i][0]=1;
                    for(int j = 0;j <m+1;j++)
                    {
                        dp[0][j]=1;
                        if(i==0||j==0)
                        {
                   // dp[i][j]=1;
                        }
                        else
                        {
                 dp[i][j]=dp[i-1][j]+dp[i][j-1];
                        }
                }
           }
          return dp[n][m];
    }
        
}

4.放苹果

题目解析:把m个苹果放到n个盘子里面,有多少种方法,
这个里面存在放或者不放两种选择,变化的参数是苹果和盘子,而且会存在重复,所以用动态规划


思路分析:

  1. dp[m][n]是把m个苹果放到n个盘子里面有多少种方法
  2. 当n>m时,无论如何都至少有n-m个空盘子,且这些盘子影响不了分发,所以d[m][n]=d[m][n-m]
  3. 当m>=n时,该问题变成了是否有空盘子的问题
  4. 当有空盘子,那么至少为一个d[m][n]=dp[m][n-1]
  5. 当没有空盘子,那么先把m个苹果分完后,再把m-n个苹果放到n个盘子里d[m][n]=dp[m-n][n]
  6. 处理初始值和边界条件,只有1个盘子,或者只有1个苹果都只有一种方法

实际操作:

  1. 定义一个二维数组int dp[][] = new dp[m+1][n+1]
  2. 定义递推公司,进行if判断
  3. 当n>m,d[m][n]=d[m][m]
  4. 当m>=n,d[m][n]=dp[m][n-1]+dp[m-n][n]
  5. 当m=1或者n=1 d[m][n] = 1
import java.util.Scanner;

public class Main
{
    public static void main(String[] args)
    {
        Scanner scanner = new Scanner(System.in);
        while(scanner.hasNext())
        {
            int m = scanner.nextInt();
            int n = scanner.nextInt();
            System.out.println(putApple(m,n));
        }
        scanner.close();
    }
    
    //创建一个函数,给两个参数,苹果数目m,盘子数目n,输出方法数
    private static int putApple(int m,int n)
    {
        if( m < 0 || n < 0 || m > 10 ||n > 10 || n == 0)
            return 0;
        int dp[][] = new int[m+1][n+1];
          for(int j = 0; j <= n; j++) {
            dp[0][j] = 1;
        }
          for ( int i = 1; i < m+1; i++)
          {
              for (int j = 1; j < n+1; j++)
              {
                  if( i == 1 || j == 1 )
                    {
                       dp[i][j] = 1;
                    }
                  if (i < j)
                  {
                      dp[i][j] = dp[i][j-1];
                  }
                  else 
                  {
                       dp[i][j] = dp[i][j-1] + dp[i-j][j];
                  }
              }
          }
          return dp[m][n];
    }
}

7.机器人动态递归

动态规划的题是指递归场景中存在大量重复计算是,加的傻缓存的优化思想:

  • 第一步先判断是否是递归;
  • 第二步进行分析看是否有重复计算;
  • 第三步设计动态规划

1.题目分析:

一行N个位置,机器人在中间位置可以往左或者往右走,方法有多少种,看到这个一般有是或者否的两种选择,且问有多少种方法。那么就可以考虑动态规划。

思路分析:(整体变化的值是当前位置和剩余步数)

  1. 定义一个函数,含义是总共N个位置,机器人在M当前位置,还剩K步,到达P位置的所有方法数。
  2. 当在1位置时,只能去2位置,且剩下k-1步,这个剩下操作跟前一步一样,只是参数不一样,进行递归调用
  3. 当在N位置时,只能去N-1位置,且剩下k-1步,这个剩下操作跟前一步一样,只是参数不一样,进行递归调用
  4. 当在中间位置 时,有两种选择,去M-1的位置和M+1的位置,且剩下k-1步,这个剩下操作跟前一步一样,只是参数不一样,进行递归调用。

2.实现操作:

  1. 定义一个函数,返回值是所有方法数,参数是N、M、K、P
  2. 定义一个二维动态数组记录历史操作:dp[i][j],代表的是当前在i位置,还剩j步,到达指定位置有多少种方法
  3. 初始化值:当i在目标位置时,dp[aim][]=1
  4. 推导公式1:双层循环,当i=1时,dp[1][j]=dp[2][j-1]
  5. 推导公式2:双层循环,当i=n时,dp[n][j]=dp[n-1][j-1]
  6. 推导公式3:双层循环,当在中间时,dp[i][j]=dp[i-1][j-1]+dp[i+1][j-1]
  7. 返回dp[M][k]
  8. 进行非法制的判断

3.机试关键代码

package com.atguigu.mr;
// 机器人当前来到的位置是cur,
// 机器人还有rest步需要去走,
// 最终的目标是aim,
// 有哪些位置?1~N
// 返回:机器人从cur出发,走过rest步之后,最终停在aim的方法数,是多少
public class Code02_RotbotWalk {
    public static void main(String[] args)
    {
        System.out.println(robot(5, 2, 4, 6));
    }
    public static int robot(int N, int start, int aim, int K)
    {
        if (N < 2 || start < 1 || start > N || aim < 1 || aim > N || K < 1) {
            return -1;
        }
        int[][] dp = new int[N + 1][K + 1];
        dp[aim][0] = 1;
        for (int rest = 1; rest <= K; rest++) {
            dp[1][rest] = dp[2][rest - 1];
            for (int cur = 2; cur < N; cur++) {
                dp[cur][rest] = dp[cur - 1][rest - 1] + dp[cur + 1][rest - 1];
            }
            dp[N][rest] = dp[N - 1][rest - 1];
        }
        return dp[start][K];
    }
}

4.总结

 8.

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
多维动态规划(MDP)是一种在多维状态空间中求解最优策略的算法。下面是解思路的一般步骤: 1. 定义状态和动作:首先,需要明确问的状态和可选的动作。将问抽象成一个多维状态空间,并确定每个状态下可执行的动作。 2. 定义价值函数:为了评估每个状态的优劣,需要定义一个价值函数来衡量状态的价值。价值函数可以是累积奖励、期望回报等。 3. 定义转移函数:转移函数描述了状态之间的转换关系,即在执行某个动作后,当前状态如何转移到下一个状态。转移函数可以是确定性的或概率性的。 4. 构建动态规划表格:根据问的状态空间和动作空间,构建一个多维表格。每个单元格代表一个状态,并记录该状态下执行不同动作所得到的价值。 5. 递归求解最优策略:从最后一个状态开始,根据动态规划的原理递归地计算每个状态的最优价值,并记录最优动作。通过向前逐步计算,可以得到整个状态空间下的最优策略。 6. 优化算法:对于复杂问,可以采用一些优化技巧来减少计算量,如值迭代、策略迭代等。 需要注意的是,多维动态规划算法的实现可能会比较复杂,涉及到状态空间的遍历和动作选择等问。因此,了解问的特点和算法的原理非常重要。 希望这个解思路能对你有所帮助!如果还有其他问,请继续提问。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值