java算法学习索引之动态规划

news2024/12/23 0:23:07

一 斐波那契数列问题的递归和动态规划

【题目】给定整数N,返回斐波那契数列的第N项。

补充问题 1:给定整数 N,代表台阶数,一次可以跨 2个或者 1个台阶,返回有多少种走法。

【举例】N=3,可以三次都跨1个台阶;也可以先跨2个台阶,再跨1个台阶;还可以先跨1个台阶,再跨2个台阶。所以有三种走法,返回3。

补充问题 2:假设农场中成熟的母牛每年只会生 1 头小母牛,并且永远不会死。第一年农场有1只成熟的母牛,从第二年开始,母牛开始生小母牛。每只小母牛3年之后成熟又可以生小母牛。给定整数N,求出N年后牛的数量。

【举例】N=6,第1年1头成熟母牛记为a;第2年a生了新的小母牛,记为b,总牛数为2;第3年a生了新的小母牛,记为c,总牛数为3;第4年a生了新的小母牛,记为d,总牛数为4。第5年b成熟了,a和b分别生了新的小母牛,总牛数为6;第6年c也成熟了,a、b和c分别生了新的小母牛,总牛数为9,返回9。

【要求】对以上所有的问题,请实现时间复杂度为O(logN)的解法。

斐波那契数列问题

奶牛问题

private int[][] multiMatrix(int[][] m1, int[][] m2) {//矩阵乘法
   // TODO Auto-generated method stub
   int[][] res=new int[m1.length][m2[0].length];
   for (int i = 0; i < m1.length; i++) {
   for (int j = 0; j < m2[0].length; j++) {
      for (int k = 0; k < m2.length; k++) {
         res[i][j]+=m1[i][k]*m2[k][j];
      }
   }
      
   }
   return res;
}

public int f3(int n)
{
   if (n<1) {
      return 0;
   }
   if (n==1||n==2) {
      return 1;
   }
   int [][] base= {{1,1},{1,0}};
   int[][] res=matrixPower(base, n-2);
   return res[0][0]+res[1][0];
}

public int c3(int n)
{
   if (n<1) {
      return 0;
   }
   if (n==1||n==2||n==3) {
      return 3;
   }
   int [][] base= {{1,0,1},{0,0,1},{1,0,0}};
   int[][] res=matrixPower(base, n-3);
   return 3*res[0][0]+2*res[1][0]+res[2][0];
}

二 矩阵的最小路径和

给定一个矩阵 m,从左上角开始每次只能向右或者向下走,最后到达右下角的位置,路径上所有的数字累加起来就是路径和,返回所有的路径中最小的路径和。

经典动态规划方法。假设矩阵 m的大小为 M×N,行数为 M,列数为 N。先生成大小和 m一样的矩阵dp,dp[i][j]的值表示从左上角(即(0,0))位置走到(i,j)位置的最小路径和。对m的第一行的所有位置来说,即(0,j)(0≤j<N),从(0,0)位置走到(0,j)位置只能向右走,所以(0,0)位置到(0,j)位置的路径和就是 m[0][0..j]这些值的累加结果。同理,对 m 的第一列的所有位置来说,即(i,0) (0≤i<M),从(0,0)位置走到(i,0)位置只能向下走,所以(0,0)位置到(i,0)位置的路径和就是m[0..i][0]这些值的累加结果。

除第一行和第一列的其他位置(i,j)外,都有左边位置(i-1,j)和上边位置(i,j-1)。从(0,0)到(i,j)的路径必然经过位置(i-1,j)或位置(i,j-1),所以,dp[i][j]=min{dp[i-1][j],dp[i][j-1]}+m[i][j],含义是比较从(0,0)位置开始,经过(i-1,j)位置最终到达(i,j)的最小路径和经过(i,j-1)位置最终到达(i,j)的最小路径之间,哪条路径的路径和更小。那么更小的路径和就是 dp[i][j]的值。

public int minPathSum1(int[][] m) {
   if (m==null||m.length==0||m[0]==null||m[0].length==0) {
      return 0;
   }
   int row=m.length;
   int col=m[0].length;
   int[][] dp=new int[row][col];
   dp[0][0]=m[0][0];
   for (int i = 1; i < row; i++) {
      dp[i][0]=dp[i-1][0]+m[i][0];
   }
   for (int j = 0; j < col; j++) {
      dp[0][j]=dp[0][j-1]+m[0][j];
   }
   for (int i = 1; i < row; i++) {
      for (int j = 0; j < col; j++) {
         dp[i][j]=Math.min(dp[i-1][j], dp[i][j-1])+m[i][j];
      }
   }
   return dp[row-1][col-1];

}

矩阵中一共有 M×N 个位置,每个位置都计算一次从(0,0)位置达到自己的最小路径和,计算的时候只是比较上边位置的最小路径和与左边位置的最小路径和哪个更小,所以时间复杂度为O(M×N),dp矩阵的大小为M×N,所以额外空间复杂度为O(M×N)。动态规划经过空间压缩后的方法。这道题的经典动态规划方法在经过空间压缩之后,时间复杂度依然是O(M×N),但是额外空间复杂度可以从O(M×N)减小至O(min{M,N}),也就是不使用大小为M×N的dp矩阵,而仅仅使用大小为min{M,N}的arr数组。具体过程如下

public int minPathSum2(int[][] m)
{
   if (m==null||m.length==0||m[0]==null||m[0].length==0) {
      return 0;
   }
   int more=Math.max(m.length, m[0].length);
   int less=Math.min(m.length, m[0].length);
   boolean rowmore= more==m.length;
   int[] arr=new int[less];
   arr[0]=m[0][0];
   for (int i = 1; i < less; i++) {
      arr[i]=arr[i-1]+(rowmore? m[0][i]:m[i][0]);//先求出到对角线的值
      
   }
   //数组  arr 中保存的是dp[i][i]中的值,但如果给定的矩阵行数小于列数(M<N),那么就生成长度为M的arr,然后令arr更新成dp矩阵每一列的值,及将arr 中的值保存为 dp[i][N]
   // 从左向右滚动过去
   for (int i = 1; i < more; i++) {
      arr[0]=arr[0]+(rowmore?m[i][0]:m[0][i]);
      for (int j = 1; j < arr.length; j++) {
         arr[j]=Math.min(arr[j-1], arr[j])+(rowmore?m[i][j]:m[j][i]);
      }
   }
   
   return arr[less-1];
   
}

三 换钱的最少货币数

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

方法一:暴力递归

public int minCoins1(int[] arr,int aim)
{
   if (arr==null||arr.length==0||aim<0) {
      return -1;
   }
   return process(arr,0,aim);
}

private int process(int[] arr, int i, int rest) {
    if (i==arr.length) {
      return rest==0?0:-1;
   }
    int res=-1;
    for (int k = 0; k * arr[i]<=rest; k++) {
      int next=process(arr, i+1, rest-k*arr[i]);
      if (next!=-1) {
         res= res==-1?next+k:Math.min(res, next+k);
      }
   }
   return res;
}

//方法二:动态规划


public int minCoins(int[] arr, int aim) {
    if (arr == null || arr.length == 0 || aim < 0) {
        return -1; // 对于非法输入,返回-1
    }
    int N = arr.length;
    // 创建一个二维数组 dp,dp[i][j] 表示使用前 i 个货币组成金额 j 的最少货币数
    int[][] dp = new int[N + 1][aim + 1];
    
    // 初始化最后一行(i=N)为-1,表示使用最后一个货币无法组成任何金额
    for (int col = 1; col <= aim; col++) {
        dp[N][col] = -1;
    }
    
    // 从倒数第二行开始向第一行递推,计算每个单元格的值
    for (int i = N - 1; i >= 0; i--) {
        for (int rest = 0; rest <= aim; rest++) {
            dp[i][rest] = -1;   // 先将当前位置的值设为-1,表示无法组成目标金额
            
            // 如果使用下一行的值能够组成当前金额,直接继承下一行的值
            if (dp[i + 1][rest] != -1) {
                dp[i][rest] = dp[i + 1][rest];
            }
            
            // 如果当前面值可以被使用,并且使用当前面值可以组成剩余金额,
            // 则更新当前位置的值为使用当前面值和不适用当前面值两种情况中的最小值
            if (rest - arr[i] >= 0 && dp[i][rest - arr[i]] != -1) {
                if (dp[i][rest] == -1) {
                    dp[i][rest] = dp[i][rest - arr[i]] + 1;
                } else {
                    dp[i][rest] = Math.min(dp[i][rest], dp[i][rest - arr[i]] + 1);
                }
            }
        }
    }
    
    // 返回组成目标金额的最少货币数
    return dp[0][aim];
}
 

这段代码使用动态规划思想,通过填表的方式计算出组成目标金额所需的最少货币数。

minCoins2 方法就是填一张 N×aim 的表,而且因为省掉了枚举过程,所以每个位置的值都在O(1)的时间内得到,该方法时间复杂度为O(N×aim)。

四  机器人达到指定位置方法数

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

(1)
public int ways1(int N, int M, int K, int P) {
    // 检查输入参数的合法性
    if (N < 2 || K < 1 || M < 1 || M > N || P < 1 || P > N) {
        return 0; // 如果参数不合法,直接返回0
    }
    // 调用 walk 方法计算机器人在K步内走到位置P的方法数
    return walk(N, M, K, P);
}

private int walk(int N, int cur, int rest, int P) {
    if (rest == 0) {
        return cur == P ? 1 : 0; // 如果已经走完K步,检查是否到达位置P,是则返回1,否则返回0
    }
    if (cur == 1) {
        return walk(N, 2, rest - 1, P); // 如果当前位置是1,则只能往右走到位置2
    }
    if (cur == N) {
        return walk(N, N - 1, rest - 1, P); // 如果当前位置是N,则只能往左走到位置N-1
    }
    // 否则,计算能够往左或往右走到下一步位置的方法总数
    return walk(N, cur + 1, rest - 1, P) + walk(N, cur - 1, rest - 1, P);
}
 

这段代码使用递归的方式计算机器人在K步内走到位置P的方法数。walk 方法负责计算具体的步数和位置情况,其中使用了递归的方式进行计算。

(2)动态规划

public int ways2(int N, int M, int K, int P) {
   if (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][j - 1];
         } else {
            dp[i][j] = dp[i - 1][j - 1] + dp[i - 1][j + 1];
         }
      }
   }
   return dp[K][M];
}

(3)动态规划+空间压缩


public int ways3(int N, int M, int K, int P) {
    if (N < 2 || K < 1 || M < 1 || M > N || P < 1 || P > N) {
        return 0; // 如果参数不合法,直接返回0
    }
    int[] dp = new int[N + 1]; // 创建一个长度为N+1的数组,用于保存每个位置的方法数
    dp[P] = 1; // 初始化目标位置P的方法数为1
    for (int i = 1; i <= K; i++) {
        int leftUp = dp[1]; // 保存上一行的左上角的值,即位置1的值
        for (int j = 1; j <= N; j++) {
            int tmp = dp[j]; // 保存当前位置的值
            if (j == 1) {
                dp[j] = dp[j + 1]; // 如果当前位置是1,只能往右走到位置2
            } else if (j == N) {
                dp[j] = leftUp; // 如果当前位置是N,只能往左走到位置N-1
            } else {
                dp[j] = leftUp + dp[j + 1]; // 否则,计算能够往左或往右走到下一步位置的方法总数
            }
            leftUp = tmp; // 更新左上角的值为上一行的当前位置的值
        }
    }
    return dp[M]; // 返回机器人在K步内走到位置M的方法数
}


这种解法利用动态规划,使用一维数组dp来记录每个位置的方法数。外层循环遍历K步,内层循环遍历每个位置,通过更新数组元素的方式计算每个位置的方法数。最后返回机器人在K步内走到位置M的方法数。

五  换钱的方法数

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

(1)暴力解法

public int coins1(int[] arr, int aim) {
   if (arr == null || arr.length == 0 || aim < 0) {
      return 0;
   }
   return process1(arr, 0, aim);
}

private int process1(int[] arr, int index, int aim) {
   // TODO Auto-generated method stub
   int res = 0;
   if (index == arr.length) {
      res = aim == 0 ? 1 : 0;
   } else {
      for (int i = 0; arr[index] * i <= aim; i++) {
         res += process1(arr, index + 1, aim - arr[index] * i);
      }
   }
   return res;

}
(2) 记忆搜索的方法
public int coins2(int[] arr, int aim) {
    if (arr == null || arr.length == 0 || aim < 0) {
        return 0; // 如果数组为空或目标钱数小于0,直接返回0
    }
    int[][] map = new int[arr.length + 1][aim + 1]; // 创建一个二维数组,用于保存每个索引和钱数对应的换钱方法数
    return process2(arr, 0, aim, map); // 调用递归函数,并返回结果
}

private int process2(int[] arr, int index, int aim, int[][] map) {
    int res = 0; // 定义结果变量res
    if (index == arr.length) { // 如果递归到达数组末尾
        res = aim == 0 ? 1 : 0; // 如果目标钱数为0,说明找到一种换钱方法,将res设为1,否则设为0
    } else {
        int mapValue = 0; // 用于存储之前计算过的换钱方法数
        for (int i = 0; arr[index] * i <= aim; i++) { // 遍历使用当前面值货币的数量
            mapValue = map[index + 1][aim - arr[index] * i]; // 查看之前计算过的换钱方法数
            if (mapValue != 0) { // 如果之前计算过
                res += mapValue == -1 ? 0 : mapValue; // 如果之前计算得到的是-1,说明之前的结果无效,不加入结果res中,否则加入结果res中
            } else { // 如果之前没有计算过
                res += process2(arr, index + 1, aim - arr[index] * i, map); // 继续递归计算剩余钱数的换钱方法数
            }
        }
    }
    map[index][aim] = res == 0 ? -1 : res; // 将计算得到的结果存入map数组中,若结果为0,则设为-1,表示无效结果
    return res; // 返回结果
}

段代码使用递归的方式解决换钱问题,并利用二维数组map来记录每个索引和钱数对应的换钱方法数。在递归函数process2中,首先判断递归是否到达了数组末尾,如果是,判断目标钱数是否为0,然后返回相应的结果。如果未到达数组末尾,则遍历使用当前面值货币的数量,并查看之前是否计算过对应的换钱方法数。如果计算过,将结果加入结果变量res中(若之前的结果为-1,说明无效,则不加入结果中),如果未计算过,则继续递归计算剩余钱数的换钱方法数。最后将计算得到的结果存入map数组中,并返回结果res。

public int coins3(int[] arr, int aim) {
    if (arr == null || arr.length == 0 || aim < 0) {
        return 0; // 如果数组为空或目标钱数小于0,直接返回0
    }
    int[][] dp = new int[arr.length][aim + 1]; // 创建一个二维数组dp,用于保存每个索引和钱数对应的换钱方法数
    for (int i = 0; i < arr.length; i++) {
        dp[i][0] = 1; // 当钱数为0时,方法数为1
    }
    for (int j = 1; arr[0] * j <= aim; j++) {
        dp[0][arr[0] * j] = 1; // 第一个面值的货币只能用来凑整数倍的自己,所以设置对应的方法数为1
    }
    int num = 0; // 用于存储临时的换钱方法数
    for (int i = 0; i < arr.length; i++) {
        for (int j = 1; j <= aim; j++) {
            num = 0; // 初始化临时变量为0
            for (int k = 0; j - arr[i] * k >= 0; k++) {
                num += dp[i - 1][j - arr[i] * k]; // 计算当前面值货币使用k张时的换钱方法数,并累加到临时变量中
            }
            dp[i][j] = num; // 将计算得到的结果存入dp数组中
        }
    }
    return dp[arr.length - 1][aim]; // 返回结果
}

这段代码使用动态规划解决换钱问题,并利用二维数组dp来记录每个索引和钱数对应的换钱方法数。首先初始化第一列,即钱数为0时的方法数都为1,因为总钱数为0时只有一种换钱方法,即什么都不换。然后初始化第一行,即第一个面值的货币只能用来凑整数倍的自己,所以设置对应的方法数为1。接下来,遍历数组中的每个面值货币和钱数,并根据状态转移方程计算当前面值货币和钱数对应的换钱方法数。最后返回dp数组的最后一个元素,即最终的结果。

可以优化为

public int coins4(int[] arr, int aim) {
    if (arr == null || arr.length == 0 || aim < 0) {
        return 0; // 如果数组为空或目标钱数小于0,直接返回0
    }
    int[][] dp = new int[arr.length][aim + 1]; // 创建一个二维数组dp,用于保存每个索引和钱数对应的换钱方法数
    for (int i = 0; i < arr.length; i++) {
        dp[i][0] = 1; // 当钱数为0时,方法数为1
    }
    for (int j = 1; arr[0] * j <= aim; j++) {
        dp[0][arr[0] * j] = 1; // 第一个面值的货币只能用来凑整数倍的自己,所以设置对应的方法数为1
    }
    for (int i = 0; i < arr.length; i++) {
        for (int j = 1; j <= aim; j++) {
            dp[i][j] = dp[i - 1][j]; // 初始化为上一个硬币的情况
            if (j - arr[i] >= 0) { // 判断当前面值的硬币是否可以组成j
                dp[i][j] += dp[i][j - arr[i]]; // 若可以组成,则加上组成j-arr[i]的情况数
            }
        }
    }
    return dp[arr.length - 1][aim]; // 返回结果
}

(4)空间压缩

public int coins5(int[] arr, int aim) {
    if (arr == null || arr.length == 0 || aim < 0) {
        return 0; // 如果数组为空或目标钱数小于0,直接返回0
    }
    int[] dp = new int[aim + 1]; // 创建一个一维数组dp,用于保存每个钱数对应的换钱方法数
    for (int j = 1; j <= aim; j++) {
        dp[arr[0] * j] = 1; // 第一个面值的货币只能用来凑整数倍的自己,所以设置对应的方法数为1
    }
    for (int i = 0; i < arr.length; i++) {
        for (int j = 1; j <= aim; j++) {
            dp[j] += j - arr[i] >= 0 ? dp[j - arr[i]] : 0; // 根据状态转移方程计算当前钱数对应的换钱方法数
        }
    }
    return dp[aim]; // 返回结果
}

这段代码使用动态规划解决换钱问题,并利用一维数组dp来记录每个钱数对应的换钱方法数。首先初始化第一个面值的货币的倍数对应的方法数为1。然后遍历数组中的每个面值货币和钱数,并根据状态转移方程计算当前钱数对应的换钱方法数。最后返回dp数组的最后一个元素,即最终的结果。这种方法优化了空间复杂度,只使用一维数组来保存动态规划的结果。

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

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

相关文章

【ARM Trace32(劳特巴赫) 使用介绍 4 - Trace32 Discovery 详细介绍】

请阅读【ARM Coresight SoC-400/SoC-600 专栏导读】 文章目录 1.1 SYS.Detect1.2 AHBAPn/AXIAPnAPBAPn.Base1.1 SYS.Detect 在 TRACE32 中, SYS.Detect 是一个用来检测目标系统配置的命令。 当你执行 SYS.Detect DAP 命令时,TRACE32 将自动检测和识别目标系统上的 ARM De…

VScode不打开浏览器实时预览html

下载Microsoft官方的Live Preview就行了 点击预览按钮即可预览

前端---CSS的样式汇总

文章目录 CSS的样式元素的属性设置字体设置文字的粗细设置文字的颜色文本对齐文本修饰文本缩进行高设置背景背景的颜色背景的图片图片的属性平铺位置大小 圆角矩形 元素的显示模式行内元素和块级元素的转化弹性布局水平方向排列方式&#xff1a;justify-content垂直方向排序方式…

GoldWave 6.78中文免费激活版功能特色2024最新功能解析

GoldWave 6.78中文免费激活版是一款多功能、强大且用户友好的音频编辑工具。它为音乐制作人、播客主持人以及音频编辑爱好者提供了全面的编辑功能&#xff0c;让你能够创造出高质量的音频作品。无论你是在音乐制作、音频修复还是播客制作&#xff0c;GoldWave 都是一个值得一试…

【Python3】【力扣题】263. 丑数

【力扣题】题目描述&#xff1a; 此题&#xff1a;正整数n&#xff0c;能被2或3或5整除&#xff0c;且不断除以2或3或5最终的数是1。 【Python3】代码&#xff1a; 1、解题思路&#xff1a;递归。 知识点&#xff1a;递归&#xff1a;函数中调用函数自身&#xff08;必须有退…

Metric

如果 Metric ‘use_polarity&#xff08;使用极性&#xff09;’ &#xff0c;则图像中的对象必须和模型具有相同的对比度&#xff08;Contrast&#xff09;。比如&#xff0c;如果模型是一个在暗/深色背景上的明亮物体&#xff0c;则仅当对象比背景更亮时才会被找到。 如果 …

Python 使用tkinter复刻Windows记事本UI和菜单功能(二)

上一篇&#xff1a;Python tkinter实现复刻Windows记事本UI和菜单的文本编辑器&#xff08;一&#xff09;-CSDN博客 下一篇&#xff1a;敬请耐心等待&#xff0c;如发现BUG以及建议&#xff0c;请在评论区发表&#xff0c;谢谢&#xff01; 相对上一篇文章&#xff0c;本篇文…

arcgis提取栅格有效边界

方法一&#xff1a;【3D Analyst工具】-【转换】-【由栅格转出】-【栅格范围】 打开一幅栅格数据&#xff0c;利用【栅格范围】工具提取其有效边界&#xff08;不包含NoData值&#xff09;&#xff1a; 方法二&#xff1a;先利用【栅格计算器】将有效值赋值为1&#xff0c;得到…

vue项目路由使用history模式,nginx配置,刷新页面显示404

需要在配置项中添加 try_files $uri $uri/ /index.html;

MySQL:语法速查手册【持续更新...】

一、定义基本表 1、常用的完整性约束 主码约束 primary key外键约束 foreign key唯一性约束 unique非空性约束 not null取值约束 check2、例题 【例1】建立一个“学生”表Student&#xff0c;由学号Sno、姓名Sname、性别Ssex、年龄Sage、所在系Sdept五个属性组成。其中…

HarmonyOS开发(一):开发工具起步

1、DevEco Studio 工具下载地址&#xff1a;HUAWEI DevEco Studio和SDK下载和升级 | HarmonyOS开发者 DevEco Studio基础配置 Node.jsOhpm 这两个都可以在进入IDE时在工具上选择下载安装 2、HelloWorld工程 打开DevEco,那么会进入欢迎页&#xff0c;点击Create Project---…

Spring事务之@EnableTransactionManagement

前言 EnableTransactionManagement 是Spring框架提供的一个注解&#xff0c;用于启用基于注解的事务管理。通过使用这个注解&#xff0c;你可以开启Spring对事务的自动管理功能&#xff0c;使得你可以在方法上使用 Transactional 注解来声明事务。 EnableTransactionManageme…

Windows环境下ADB调试——安装adb

一、下载 Windows版本&#xff1a;https://dl.google.com/android/repository/platform-tools-latest-windows.zipMac版本&#xff1a;https://dl.google.com/android/repository/platform-tools-latest-darwin.zipLinux版本&#xff1a;https://dl.google.com/android/reposit…

CPD:使用restAPI和cpd-cli命令创建DMC实例

环境 Red Hat Enterprise Linux release 8.6 (Ootpa)OCP 4.12.22IBM CP4D 4.8.0Data Management Console 3.1.12 (DMC for CPD 4.8.0) 注&#xff1a;使用了fyre VM。 创建DMC实例 准备 首先export环境变量&#xff1a; . ./stg_env.sh把 cpd-cli 放到PATH里。编辑 ~/.ba…

【KCC@南京】KCC南京数字经济-开源行

一场数字经济与开源的视听盛宴&#xff0c;即将于11月26日&#xff0c;在南京举办。本次参与活动的有&#xff1a; 庄表伟&#xff08;开源社理事执行长、天工开物开源基金会执行副秘书长&#xff09;、林旅强Richard&#xff08;开源社联合创始人、前华为开源专家&#xff09;…

ElasticSearch简单操作

目录 1.单机部署 1.1 解压软件 1.2 创建软链接 1.3 修改配置文件 1.4 配置环境变量 1.5 后台启动 2.配置分词器 2.1 安装IK分词器 2.2 ES 扩展词汇 3.常用操作 3.1 索引 3.1.1 创建索引 3.1.2 查看所有索引 3.1.3 查看单个索引 3.1.4 删除索引 3.2.文档 3.2.1…

排序算法之-快速

算法原理 丛待排序的数列中选择一个基准值&#xff0c;通过遍历数列&#xff0c;将数列分成两个子数列&#xff1a;小于基准值数列、大于基准值数列&#xff0c;准确来说还有个子数列&#xff1a;等于基准值即&#xff1a; 算法图解 选出基准元素pivot&#xff08;可以选择…

【藏经阁一起读】(76)__《“DNS+”发展白皮书》

【藏经阁一起读】&#xff08;76&#xff09;__《“DNS”发展白皮书》 作者&#xff1a; 梁卓 宋林健 陈剑 刘志辉 刘保君 郭丰 马晨迪 马永 孙俊哲 沈建伟 嵇叶楠 孙宛月 张建光 李贤达 张晓军 赵华 发布时间&#xff1a;2023-10-31 章节数&#xff1a;6 一、基础知识 1.1、…

《深入浅出进阶篇》洛谷P4147 玉蟾宫——悬线法dp

上链接&#xff1a;P4147 玉蟾宫 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)https://www.luogu.com.cn/problem/P4147 上题干&#xff1a; 有一个NxM的矩阵&#xff0c;每个格子里写着R或者F。R代表障碍格子&#xff0c;F代表无障碍格子请找出其中的一个子矩阵&#xff0c…