动态规划从入门到精通

目录

动态规划的详解

动态规划的应用

     机器人到达指定位置数

     换钱的最少货币数

     排成一条线的纸牌博弈问题

     象棋中马的跳法

     Bob的生存概率 

     换钱的方法数 

动态规划的总结

动态规划的详解

       暴力尝试递归操作中有很多重复计算的操作,浪费时间。动态规划就是减少暴力尝试中重复计算的技巧,这种技巧就是一个大型套路,先写出用尝试的思路解决问题的递归函数,而不用操心时间复杂度,这个过程是无可替代的,没有套路的,只能依靠个人智慧或者足够多的经验。

       但是怎么把尝试的版本,优化成动态规划,是有固定套路的,大体步骤如下:

       (1)找到什么可变参数可以代表一个递归状态,也就是哪些参数一旦确定,返回值就确定了;

       (2)把可变参数的所有组合映射成一张表,有 1 个可变参数就是一维表,2 个可变参数就是二维表,......

       (3)最终答案要的是表中的哪个位置,在表中标出;

       (4)根据递归过程的 base case,把这张表的最简单、不需要依赖其他位置的那些位置填好值;

       (5)根据递归过程非base case的部分,也就是分析表中的普遍位置需要怎么计算得到,那么这张表的填写顺序也就确定了;

       (6)填好表,返回最终答案在表中位置的值;

       对于代码方面的修改也是有固定套路的,对于记忆化搜索的方法就是首先写出尝试的思路解决问题的递归函数,然后在此基础上先改成记忆化搜索的程序,也就是添加上数组,记录计算过的值,避免出现重复计算的过程,后面执行程序时对于计算过的直接使用不再重复计算。

       严格位置表依赖的方法是将按照上面的步骤将目标值和初始确定的值在程序中先确定出来,然后对递归程序进行适当更改即可完成。

动态规划的应用

     机器人到达指定位置数

     【题目】 假设有排成一行的N个位置,记为1~N,N 一定大于或等于 2。开始时机器人在其中的 M 位置上(M一定是 1~N 中的一个),机器人可以往左走或者往右走,如果机器人来到1位置,那么下一步只能往右来到2 位置;如果机器人来到N位置,那么下一步只能往左来到 N-1 位置。规定机器人必须走K步,最终能来到P位置(P 也一定是 1~N 中的一个)的方法有多少种。给定四个参数 N、M、K、P,返回方法数。

     【举例】 N=5,M=2,K=3,P=3 上面的参数代表所有位置为1 2 3 4 5。机器人最开始在2位置上,必须经过3步,最后到达3位置。走的方法只有如下3种: (1)从2到1,从1到2,从2到3 (2)从2到3,从3到2,从2到3 (3)从2到3,从3到4,从4到3。所以返回方法数3。 N=3,M=1,K=3,P=3 上面的参数代表所有位置为1 2 3。机器人最开始在1位置上,必须经过3步,最后到达3位置。怎么走也不可能,所以返回方法数0。

    public static int ways1(int N, int M, int K, int P) {//使用暴力递归的方式解决问题,时间复杂度能达到O(2^k)// 参数无效直接返回0if (N < 2 || K < 1 || M < 1 || M > N || P < 1 || P > N) {return 0;}// 总共N个位置,从M点出发,还剩K步,返回最终能达到P的方法数return walk(N, M, K, P);}// N : 位置为1 ~ N,固定参数// cur : 当前在cur位置,可变参数// rest : 还剩res步没有走,可变参数// P : 最终目标位置是P,固定参数// 该函数的含义:只能在1~N这些位置上移动,当前在cur位置,走完rest步之后,停在P位置的方法数作为返回值返回public static int walk(int N, int cur, int rest, int P) {// 如果没有剩余步数了,当前的cur位置就是最后的位置// 如果最后的位置停在P上,那么之前做的移动是有效的// 如果最后的位置没在P上,那么之前做的移动是无效的if (rest == 0) {return cur == P ? 1 : 0;}// 如果还有rest步要走,而当前的cur位置在1位置上,那么当前这步只能从1走向2// 后续的过程就是,来到2位置上,还剩rest-1步要走if (cur == 1) {return walk(N, 2, rest - 1, P);}// 如果还有rest步要走,而当前的cur位置在N位置上,那么当前这步只能从N走向N-1// 后续的过程就是,来到N-1位置上,还剩rest-1步要走if (cur == N) {return walk(N, N - 1, rest - 1, P);}// 如果还有rest步要走,而当前的cur位置在中间位置上,那么当前这步可以走向左,也可以走向右// 走向左之后,后续的过程就是,来到cur-1位置上,还剩rest-1步要走// 走向右之后,后续的过程就是,来到cur+1位置上,还剩rest-1步要走// 走向左、走向右是截然不同的方法,所以总方法数要都算上return walk(N, cur + 1, rest - 1, P) + walk(N, cur - 1, rest - 1, P);}public static int ways2(int N, int M, int K, int P) {//使用记忆化搜索的方式解决问题,时间复杂度为O(M*K)// 参数无效直接返回0if (N < 2 || K < 1 || M < 1 || M > N || P < 1 || P > N) {return 0;}int[][] dp = new int[K + 1][N + 1];//定义一个数组存放计算过的内容,作为缓存结构for(int i=0;i<=k;i++){for(int j=0;j<=N;j++){dp[i][j]= -1;}}// 总共N个位置,从M点出发,还剩K步,返回最终能达到P的方法数return walk2(N, M, K, P , dp);}// N : 位置为1 ~ N,固定参数// cur : 当前在cur位置,可变参数// rest : 还剩res步没有走,可变参数// P : 最终目标位置是P,固定参数// 该函数的含义:只能在1~N这些位置上移动,当前在cur位置,走完rest步之后,停在P位置的方法数作为返回值返回public static int walk2(int N, int cur, int rest, int P,int[][] dp) {if(dp[rest][cur]!=-1){//如果某个值已经计算过,不需要再重复计算,直接返回return dp[rest][cur];}//还没有计算过,每次返回之前把答案记录下来if (rest == 0) {dp[rest][cur] = cur == P ? 1 : 0;return dp[rest][cur];}// 如果还有rest步要走,而当前的cur位置在1位置上,那么当前这步只能从1走向2// 后续的过程就是,来到2位置上,还剩rest-1步要走if (cur == 1) {dp[rest][cur] =walk2(N, 2, rest - 1, P);}// 如果还有rest步要走,而当前的cur位置在N位置上,那么当前这步只能从N走向N-1// 后续的过程就是,来到N-1位置上,还剩rest-1步要走else if (cur == N) {dp[rest][cur] =  walk2(N, N - 1, rest - 1, P);}else{dp[rest][cur] = walk2(N, cur + 1, rest - 1, P) + walk2(N, cur - 1, rest - 1, P);}return dp[rest][cur];}public static int ways3(int N, int M, int K, int P) {//严格位置表依赖的方式// 参数无效直接返回0if (N < 2 || K < 1 || M < 1 || M > N || P < 1 || P > N) {return 0;}int[][] dp = new int[K + 1][N + 1];//定义一个数组存放计算过的内容dp[0][P] = 1;//终点的位置在格子中标出来for (int i = 1; i <= K; i++) {//然后从第一行第一列开始,下面的过程根据递归的依赖性,进行改编for (int j = 1; j <= N; j++) {if (j == 1) {dp[i][j] = dp[i - 1][2];} else if (j == N) {dp[i][j] = dp[i - 1][N - 1];} else {dp[i][j] = dp[i - 1][j - 1] + dp[i - 1][j + 1];}}}return dp[K][M];}

     换钱的最少货币数

     【题目】给定数组 arr,arr中所有的值都为正数且不重复。每个值代表一种面值的货币,每种面值的货币可以使用任意张,再给定一个整数 aim,代表要找的钱数,求组成aim的最少货币数。

     【举例】arr=[5,2,3],aim=20。4 张 5 元可以组成 20 元,其他的找钱方案都要使用更多张的货币,所以返回 4。arr=[5,2,3],aim=0。不用任何货币就可以组成0元,返回 0。arr=[3,5],aim=2。根本无法组成2元,钱不能找开的情况下默认返回-1。

    public static int minCoins1(int[] arr, int aim) {//暴力递归方式求解if (arr == null || arr.length == 0 || aim < 0) {return -1;}return process(arr, 0, aim);}// 当前考虑的面值是arr[i],还剩rest的钱需要找零// 如果返回-1说明自由使用arr[i..N-1]面值的情况下,无论如何也无法找零rest// 如果返回不是-1,代表自由使用arr[i..N-1]面值的情况下,找零rest需要的最少张数public static int process(int[] arr, int i, int rest) {if(rest < 0){return -1;}if(rest == 0){return 0;}//rest>0但是没有钱if(i == arr.length){return -1;}//rest > 0并且也有硬币,有两种选择int p1 = process(arr,i+1,rest);//表示不要下一个硬币int p2Next = process(arr,i+1,rest-arr[i]);//表示要下一个硬币if(p1 == -1&&p2Next == -1){return -1;}else{if(p1 = -1){return p2Next +1;//加1是因为p2Next表示要下一个硬币}if(p2 = -1){return p1;}return Math.min(p1,p2Next+1);}}public static int minCoins2(int[] arr, int aim) {//记忆化搜索的动态规划的方式求解if (arr == null || arr.length == 0 || aim < 0) {return -1;}int[][] dp = new int[arr.length+1][aim+1];//建立数组记录计算的过程for(int i = 0;i <= arr.length; i++){//初始化for(int j = 0;j <= aim; j++){dp[i][j] = -2;}}return process2(arr, 0, aim,dp);}// 当前考虑的面值是arr[i],还剩rest的钱需要找零// 如果返回-1说明自由使用arr[i..N-1]面值的情况下,无论如何也无法找零rest// 如果返回不是-1,代表自由使用arr[i..N-1]面值的情况下,找零rest需要的最少张数public static int process2(int[] arr, int i, int rest , int[][] dp) {if(rest < 0){return -1;}if(dp[i][rest]!=-2){//如果已经计算过,直接返回return dp[i][rest];}if(rest == 0){dp[i][rest] = 0;}else if(i == arr.length){dp[i][rest] = -1;}else{//rest > 0并且也有硬币,有两种选择int p1 = process2(arr,i+1,rest,dp);//表示不要下一个硬币int p2Next = process2(arr,i+1,rest-arr[i],dp);//表示要下一个硬币if(p1 == -1&&p2Next == -1){dp[i][rest] = -1;}else{if(p1 = -1){dp[i][rest] = p2Next +1;//加1是因为p2Next表示要下一个硬币}else if(p2 = -1){dp[i][rest] = p1;}else{dp[i][rest] = Math.min(p1,p2Next+1);}}}return dp[i][rest];}public static int minCoins3(int[] arr, int aim) {//严格表结构的动态规划方式求解if (arr == null || arr.length == 0 || aim < 0) {return -1;}int N = arr.length;int[][] dp = new int[N + 1][aim + 1];// 设置最后一排的值,除了dp[N][0]为0之外,其他都是-1//一些知道的初始位置设置好for (int col = 1; col <= aim; col++) {dp[N][col] = -1;}for(int row = 0;row <= N;row++){dp[row][0] = 0;}//把递归的过程放过来,然后根据表结构进行适当的改动for(int i = N-1; i>= 0;i--){for(int rest = 1;rest <= aim;rest++){int p1 = dp[i+1][rest];int p2Next = -1;if(rest - arr[i] >= 0){p2Next = dp[i+1][rest - arr[i]];}if(p1 == -1&&p2Next == -1){dp[i+1][rest] = -1;}else{if(p1 = -1){dp[i+1][rest] = p2Next +1;//加1是因为p2Next表示要下一个硬币}if(p2 = -1){dp[i+1][rest] = p1;}dp[i+1][rest] = Math.min(p1,p2Next+1);}}}return dp[0][aim];}

     排成一条线的纸牌博弈问题

     【题目】给定一个整型数组 arr,代表数值不同的纸牌排成一条线。玩家A和玩家B依次拿走每张纸牌,规定玩家A先拿,玩家B后拿,但是每个玩家每次只能拿走最左或最右的纸牌,玩家A和玩 家B都绝顶聪明。请返回最后获胜者的分数。

     【举例】arr=[1,2,100,4]。开始时,玩家A只能拿走1或4。如果玩家A拿走1,则排列变为[2,100,4],接下来玩家B可以拿走2或4,然后继续轮到玩家A。如果开始时玩家A拿走4,则排列变为[1,2,100],接下来玩家B可以拿走1或100,然后继续轮到玩家A。玩家A作为绝顶聪明的人不会先拿4,因为拿4之后,玩家B将拿走100。所以玩家A会先拿1,让排列变为[2,100,4],接下来玩家B 不管怎么选,100都会被玩家A拿走。玩家A会获胜,分数为101。所以返回101。arr=[1,100,2]。 开始时,玩家A不管拿1还是2,玩家B作为绝顶聪明的人,都会把100拿走。玩家B会获胜,分数为 100。所以返回100。

    public static int win1(int[] arr) {//暴力递归的方式求解if (arr == null || arr.length == 0) {return 0;}return Math.max(f(arr, 0, arr.length - 1), s(arr, 0, arr.length - 1));//先手和后手谁的分数多,谁获胜}public static int f(int[] arr, int i, int j) {//先手函数if (i == j) {//如果只有一个数字,先手直接拿了return arr[i];}return Math.max(arr[i] + s(arr, i + 1, j), arr[j] + s(arr, i, j - 1));//如果不是只有一个数字,那么先手选择拿左边和右边两种情况下,最大的那一种情况}public static int s(int[] arr, int i, int j) {//后手函数if (i == j) {//如果只有一张牌,后手拿不到return 0;}return Math.min(f(arr, i + 1, j), f(arr, i, j - 1));//如果不只有一张牌,后手只能拿到剩下情况下最小的那种情况}//在范围上尝试的模型,行是不可能超过列的,左下角区域都是不存在的,先填对角线//动态规划一定要画图操作,用最基础的递归操作进行画图,找到格子之间的关系,然后递归的过程进行改写public static int win2(int[] arr) {if (arr == null || arr.length == 0) {return 0;}//建立两个格子int[][] f = new int[arr.length][arr.length];int[][] s = new int[arr.length][arr.length];for (int j = 0; j < arr.length; j++) {f[j][j] = arr[j];//对角线元素填上s[j][j] = 0;for (int i = j - 1; i >= 0; i--) {//只对右上角进行操作,两个表互相依赖f[i][j] = Math.max(arr[i] + s[i + 1][j], arr[j] + s[i][j - 1]);s[i][j] = Math.min(f[i + 1][j], f[i][j - 1]);}}return Math.max(f[0][arr.length - 1], s[0][arr.length - 1]);}

     象棋中马的跳法

     【题目】请同学们自行搜索或者想象一个象棋的棋盘,然后把整个棋盘放入第一象限,棋盘的最左下角是(0,0)位置。那么整个棋盘就是横坐标上9条线、纵坐标上10条线的一个区域。给你三个 参数,x,y,k,返回如果“马”从(0,0)位置出发,必须走k步,最后落在(x,y)上的方法数有多少种?

    public static int getWays(int x, int y, int step) {//暴力递归的方式求解return process(x, y, step);}public static int process(int x, int y, int step) {if (x < 0 || x > 8 || y < 0 || y > 9) {return 0;}//x,y位置越界,0种方法,无法到达if (step == 0) {//不能再动了return (x == 0 && y == 0) ? 1 : 0;//一开始在(0,0)位置,如果想要到达的就是(0,0)位置,那么已经到达,一种方法,如果不是那么无法到达}//不越界也可以跳,把跳一步可以跳到(x,y)位置的情况都写出来return process(x - 1, y + 2, step - 1)+ process(x + 1, y + 2, step - 1)+ process(x + 2, y + 1, step - 1)+ process(x + 2, y - 1, step - 1)+ process(x + 1, y - 2, step - 1)+ process(x - 1, y - 2, step - 1)+ process(x - 2, y - 1, step - 1)+ process(x - 2, y + 1, step - 1);}public static int dpWays(int x, int y, int step) {//严格表结构的动态规划方式求解//有三个可变参数,那么建立一个三维立体,其它的按照递归的程序和立体图形各个之间的关系进行改写if (x < 0 || x > 8 || y < 0 || y > 9 || step < 0) {return 0;}//这个立体之外的部分都是0int[][][] dp = new int[9][10][step + 1];//建立一个立体dp[0][0][0] = 1;//第0层的面只有(0,0)位置是1,其它都是0for (int h = 1; h <= step; h++) {//每一层处理,每一层只依赖于下一层的内容for (int r = 0; r < 9; r++) {for (int c = 0; c < 10; c++) {dp[r][c][h] += getValue(dp, r - 1, c + 2, h - 1);dp[r][c][h] += getValue(dp, r + 1, c + 2, h - 1);dp[r][c][h] += getValue(dp, r + 2, c + 1, h - 1);dp[r][c][h] += getValue(dp, r + 2, c - 1, h - 1);dp[r][c][h] += getValue(dp, r + 1, c - 2, h - 1);dp[r][c][h] += getValue(dp, r - 1, c - 2, h - 1);dp[r][c][h] += getValue(dp, r - 2, c - 1, h - 1);dp[r][c][h] += getValue(dp, r - 2, c + 1, h - 1);}}}return dp[x][y][step];}public static int getValue(int[][][] dp, int row, int col, int step) {//防止越界的函数,如果越界取0,如果没有越界,拿到相应位置的值if (row < 0 || row > 8 || col < 0 || col > 9) {return 0;}return dp[row][col][step];}

     Bob的生存概率 

     【题目】给定五个参数n,m,i,j,k。表示在一个N*M的区域,Bob处在(i,j)点,每次Bob等概率的向上、下、左、右四个方向移动一步,Bob必须走K步。如果走完之后,Bob还停留在这个区域上, 就算Bob存活,否则就算Bob死亡。请求解Bob的生存概率,返回字符串表示分数的方式。

   public static String bob1(int N, int M, int i, int j, int K) {//暴力递归的方式求解long all = (long) Math.pow(4, K);//总的方法数位4的k次方,因为每一个位置的选择有4种,一共走k步long live = process(N, M, i, j, K);long gcd = gcd(all, live);//概率就是活下来的除以总的return String.valueOf((live / gcd) + "/" + (all / gcd));}public static long process(int N, int M, int row, int col, int rest) {if (row < 0 || row == N || col < 0 || col == M) {return 0;}//如果越界,死亡if (rest == 0) {//如果已经走完也没有越界,活下来return 1;}//Bob总体活下来的方法数,等于他往上,往下,往左,往右分别走一步且活下来的方法数long live = process(N, M, row - 1, col, rest - 1);live += process(N, M, row + 1, col, rest - 1);live += process(N, M, row, col - 1, rest - 1);live += process(N, M, row, col + 1, rest - 1);return live;}public static long gcd(long m, long n) {//求最大公约数return n == 0 ? m : gcd(n, m % n);}public static String bob2(int N, int M, int i, int j, int K) {//严格表结构的动态规划的方式,同样的按照递归的方式,分析立体结构的关系求解int[][][] dp = new int[N + 2][M + 2][K + 1];//建立一个立体for (int row = 1; row <= N; row++) {for (int col = 1; col <= M; col++) {dp[row][col][0] = 1;}}for (int rest = 1; rest <= K; rest++) {for (int row = 1; row <= N; row++) {for (int col = 1; col <= M; col++) {dp[row][col][rest] = dp[row - 1][col][rest - 1];dp[row][col][rest] += dp[row + 1][col][rest - 1];dp[row][col][rest] += dp[row][col - 1][rest - 1];dp[row][col][rest] += dp[row][col + 1][rest - 1];}}}long all = (long) Math.pow(4, K);long live = dp[i + 1][j + 1][K];long gcd = gcd(all, live);return String.valueOf((live / gcd) + "/" + (all / gcd));}

     换钱的方法数 

       有给定面值的零钱数在arr数组中,最终需要找零的钱数为aim,返回最终能够找零的方法数。

public static int way1(int[] arr, int aim){//暴力递归方法的求解return process(arr,0,aim);//可以使用arr[0..]中的所有面值
}
//可以自由使用arr[index..]所有的面值
pubilc static int process(int[] arr,int index,int rest){if(index == arr.length){//如果已经没有钱数可以选择return rest == 0? 1:0;//那么如果不需要货币,只有一种方法,其它的返回0}int ways = 0;for(int zhang = 0; arr[index] * zhang <= rest; zhang ++){//只要选择的面值乘以张数不超过总计需要的,就可以随便选ways += process(arr,index + 1,rest - arr[index] * zhang);}return ways;
}
public static int ways2(int[] arr,int aim){//严格表结构的动态规划的方式求解,没有优化枚举结构,还是对递归方式进行适当的改动即可if(arr == null||arr.length == 0){return 0;}int N = arr.length;int[][] dp = new int[N+1][aim+1];dp[N][0] = 1;for(int index = N-1;index >= 0;index--){for(int rest = 0;rest <= aim;rest++){int ways = 0;for(int zhang = 0;arr[index] * zhang <= rest;zhang ++){ways += dp[index+1][rest - arr[index] * zhang];}dp[index][rest] = ways;}}return dp[0][aim];
} 
public static int ways3(int[] arr,int aim){//严格表结构的动态规划的方式求解,优化枚举结构,其实也就是通过对格子中位置求解的观察,发现枚举行为和周围格子的关系,利用这个关系减少优化(称为斜率优化),对于同一行重复需要的内容,不再重新计算if(arr == null||arr.length == 0){return 0;}int N = arr.length;int[][] dp = new int[N+1][aim+1];dp[N][0] = 1;for(int index = N-1;index >= 0;index--){for(int rest = 0;rest <= aim;rest++){dp[index][rest] += dp[index][rest];//一个新的需要计算的格子,一定需要它下面的格子。if(rest - arr[index] >= 0){//如果还没有凑够dp[index][rest] += dp[index][rest - arr[index]];//加上自己同行减去本行的面值位置的值}}}return dp[0][aim];
} 

动态规划的总结

       动态规划首先最重要的就是尝试,尝试的方式有从左到右以及范围尝试等比较重要的尝试方法,然后根据对题目的分析,写出暴力递归方式的代码,此时加上一个缓存数组,减少重复内容的重复计算,也就是改写成记忆化搜索的动态规划方式,此时并没有研究各个变量之间的依赖性,只是加了一个缓存结构。后面再根据这些关系,画出严格表结构,根据一些知道的内容,对表架构进行填充,表中需要求解的位置,根据记忆化搜索的代码和暴力递归的代码,分析出各个格子之间的关系,此时就可以根据暴力递归的代码该写出严格表结构的动态规划的代码,写出严格表结构进行分析能够对类似于枚举行为的结构进行优化,这是非常重要的。

       而尝试方法的好坏考虑的有两个方面,一是可变参数的个数,可变参数的个数越少,分析严格表结构时维度越低,更简单。二是单可变参数的维度,也就是一个参数的维度最好就是一个整数,这个是一定要保证的。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/164586.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

大模型增量预训练参数说明

在增量预训练过程中通常需要设置三类或四类参数,模型参数,数据参数,训练参数,额外参数。 下面分别针对这四种参数进行说明。 欢迎关注公众号 模型参数 model_type模型类型,例如bloom,llama,baichuan,qwen等。 model_name_or_path模型名称或者路径。 tokenizer_name_or…

JS数组常用的20种方法详解(每一个方法都有例子,超全面,超好理解的教程,干货满满)

目录 1.会改变原数组的方法&#xff08;7种&#xff09; 1.push() 2.pop() 3.unshift() 4.shift() 5.reverse() 6.sort() 7.splice() 2.不改变原数组的方法&#xff08;13种&#xff0c;返回的新数组是从原数组浅拷贝来的&#xff09; 1.concat() 2.join() 3.slice…

12个最佳WordPress投票插件

您是否正在为您的网站寻找WordPress投票插件&#xff1f; WordPress投票插件可让您轻松地在您的网站上进行民意调查&#xff0c;用户可以投票。这是在收集见解的同时建立用户参与度的有效策略。 在本文中&#xff0c;我们精心挑选了最好的WordPress投票插件&#xff0c;可帮助…

代码随想录算法训练营第五十二天|300.最长递增子序列 674. 最长连续递增序列 718. 最长重复子数组

文档讲解&#xff1a;代码随想录 视频讲解&#xff1a;代码随想录B站账号 状态&#xff1a;看了视频题解和文章解析后做出来了 300.最长递增子序列 class Solution: # 2516 ms, faster than 64.96%def lengthOfLIS(self, nums: List[int]) -> int:n len(nums)dp [1] * n…

从Discord的做法中学习 — 使用Golang进行请求合并

正如你可能之前看到的&#xff0c;Discord去年发布了一篇有价值的文章&#xff0c;讨论了他们成功存储了数万亿条消息。虽然有很多关于这篇文章的YouTube视频和文章&#xff0c;但我认为这篇文章中一个名为“数据服务为数据服务”的部分没有得到足够的关注。在这篇文章中&#…

QT项目移植到VS+QT(RTI-DDS)

QT中.pro文件中include(./xxx.pri) pri文件如下定义 unset(FILENAMES)for(FILENAME, FILENAMES) {HEADERFILE $$PWD/$${FILENAME}.hif(exists($$HEADERFILE)) {HEADERS * $$HEADERFILE}SOURCEFILE $$PWD/$${FILENAME}.cppif(exists($$SOURCEFILE)) {SOURCES * $$SOURCEFILE}…

CSS-鼠标属性篇

属性名&#xff1a;cursor 功能&#xff1a;设置鼠标光标的样式 属性值&#xff1a; pointer&#xff1a;小手move&#xff1a;移动图标text&#xff1a;文字选择器crosshair&#xff1a;十字架wait&#xff1a;等待help&#xff1a;帮助 eg.html{ cursor: wait;}(此处使用css改…

SpringBoot——MVC原理

优质博文&#xff1a;IT-BLOG-CN 一、SpringMVC自动配置 SpringMVC auto-configuration&#xff1a;SpringBoot自动配置好了SpringMVC。以下是SpringBoot对SpringMVC的默认配置&#xff1a;[WebMvcAutoConfiguration] 【1】包括ContentNegotiatingViewResolver和BeanNameView…

Keil工程打开发现目标芯片无法选择解决方案

买了一个开发板&#xff0c;配套有一些底层驱动的例程&#xff0c;打开后发现目标芯片无法选择&#xff0c;对应的下载Flash FLM文件也无法选择。从提示框中可以知道所提供的例程是Keil4的例程&#xff0c;我电脑上安装的Keil版本是Keil版本&#xff0c;估计是这个原因导致工程…

C# 执行Excel VBA宏工具类

写在前面 在Excel文档的自动化处理流程中&#xff0c;有部分值需要通过已定义的宏来求解&#xff0c;所以延伸出了用C# 调用Excel中的宏代码的需求。 首先要从NuGet中引入Microsoft.Office.Interop.Excel 类库 using Excel Microsoft.Office.Interop.Excel; 代码实现 /// &l…

HashMap,1.7与1.8的区别,HashMap的扩容方式有哪些

HashMap,1.7与1.8的区别 底层数据结构的区别 JDK 1.8之前&#xff1a; 1&#xff09;JDK1.8 之前HashMap 底层是数组和链表结合在一起使用也就是链表散列。 2&#xff09;HashMap 通过key 的hashCode 经过扰动函数处理过后得到hash 值&#xff0c;然后通过(n - 1&#xff09…

修改el-radio-group样式,自定义单选组件

修改el-radio-group样式,自定义单选组件 自定义组件 MyRadioGroup.vue <template><div class"btnsBox"><el-radio-group v-model"activeIndex" change"handleClick"><el-radio-buttonv-for"(item, index) in list&qu…

CSS3动画

在CSS3中新增了一个很有意思的东西&#xff0c;那就是动画&#xff0c;有了动画我们可以做很多的事情&#xff0c;让我为大家介绍一下动画吧&#xff01; 本篇文章关于介绍动画&#xff0c;利用小球移动为你们介绍一下动画 默认样式&#xff1a; <!DOCTYPE html> <ht…

普通话考试相关(一文读懂)

文章目录&#xff1a; 一&#xff1a;相关常识 1.考试报名时间 2.报名地方 费用 证件 3.考试流程 4.普通话等级说明 二&#xff1a;题型 三&#xff1a;技巧 1.前三题 2.命题说话 四&#xff1a;普通话考试题库 1.在线题库 2.下载题库 一&#xff1a;相关常识 …

JavaEE(SpringMVC)期末复习

文章目录 JavaEE期末复习一、单选题&#xff1a; JavaEE期末复习 一、单选题&#xff1a; 1.Spring的核⼼技术是&#xff08; A &#xff09;&#xff1f; A依赖注入 B.JdbcTmplate C.声明式事务 D.资源访问 Spring的核心技术包括依赖注入&#xff08;Dependency Injection&am…

【前端】js通过canvas获取浏览器的唯一指纹可以当做唯一标识

【前端】js通过canvas获取浏览器的唯一指纹可以当做唯一标识 <!DOCTYPE html> <html><head> <meta charset"utf-8" /> <meta name"viewport" content"widthdevice-width" /> <title>JS Bin</title> &…

解决Emmy Lua插件在IDEA或 Reder 没有代码提示的问题(设置文件关联 增加对.lua.txt文件的支持)

目录 Reder版本2019.x Reder版本2021.1.5x Reder版本2019.x 解决Emmy Lua插件在IDEA或 Reder 没有代码提示的问题(设置文件关联 增加对.lua.txt文件的支持) Reder版本2021.1.5x 解决Emmy Lua插件在IDEA或 Reder 没有代码提示的问题(设置文件关联 增加对.lua.txt文件的支持)…

java游戏制作-王者荣耀游戏

一.准备工作 首先创建一个新的Java项目命名为“王者荣耀”&#xff0c;并在src下创建两个包分别命名为“com.sxt"、”com.stx.beast",在相应的包中创建所需的类。 创建一个名为“img”的文件夹来储存所需的图片素材。 二.代码呈现 package com.sxt;import javax.sw…

Netty Review - 探索ByteBuf的内部机制

文章目录 概念ByteBuf VS Java NIO BufferByteBuf实现类HeapByteBuf vs DirectByteBufPooledByteBuf vs UnpooledByteBuf其他 ByteBuf的实现机制 概念 ByteBuf是Netty中用于处理二进制数据的缓冲区 Netty的ByteBuf是一个可用于高效存储和操作字节数据的数据结构。与传统的Byt…

跳跃游戏[中等]

优质博文&#xff1a;IT-BLOG-CN 一、题目 给你一个非负整数数组nums&#xff0c;你最初位于数组的第一个下标 。数组中的每个元素代表你在该位置可以跳跃的最大长度。判断你是否能够到达最后一个下标&#xff0c;如果可以&#xff0c;返回true&#xff1b;否则&#xff0c;返…