LeetCode 120 Triangle

  • 1.分治 (超时)

递归、分治、动态规划本质上都是去找重复子问题,那么这道题的重复子问题或者说是重复性是什么呢?

[
      [2],
     [3,4],
    [6,5,7],
   [4,1,8,3]
]

我们拿第二层来说吧:
从第一层到第二层就是:2 + min(3及后面数的和,4及后面数的和),到第三层也是这样的,那么现在我们就不要人肉递归了,下面的方程就是我们找到的重复性,怎么感觉和数学里面推导公式一样,好像就是推导公式哈

int minimumTotal(int** triangle, int triangleSize, int* triangleColSize){
    int ret;
    ret = recursion(triangle,triangleSize,triangleColSize,0,0);
    return ret;
}

int recursion(int** triangle,int triangleSize,int* triangleColSize,int level,int column) {
    // terminator
    if (level == triangleSize-1) {
        return triangle[level][column];
    }
//process current login and drill down
    int left = recursion(triangle,triangleSize,triangleColSize,level+1,column);
    int right = recursion(triangle,triangleSize,triangleColSize,level+1,column+1);
    int minNum = left < right ? left : right;
    
    return minNum + triangle[level][column];
}
  • 2.添加记忆化数组 ac

重复计算的字在哪,一开始的时候我想不清楚,就手动在纸上画了一下,下面是我在电脑上画的:


你会发现里面很多是重复计算的,所以我们可以加一个记忆化数组来避免这些重复计算

int minimumTotal(int** triangle, int triangleSize, int* triangleColSize){
    if (triangleSize <= 0) {
        return 0;
    }
    
    int ret;
    int **memo = calloc(triangleSize,sizeof(int *));
    int i;
    for (i = 0;i < triangleSize;i++) {
        memo[i] = calloc(triangleSize,sizeof(int));
    }
    
    ret = recursion(triangle,triangleSize,triangleColSize,0,0,memo);
    return ret;
}

int recursion(int** triangle,int triangleSize,int* triangleColSize,int level,int column,int **memo) {
    
    if (memo[level][column] != 0) {
        return memo[level][column];
    }
    
    // terminator
    if (level == triangleSize-1) {
        return triangle[level][column];
    }
    
    int left = recursion(triangle,triangleSize,triangleColSize,level+1,column,memo);
    int right = recursion(triangle,triangleSize,triangleColSize,level+1,column+1,memo);
    int minNum = left < right ? left : right;
    
    if (memo[level][column] == 0) {
        memo[level][column] = minNum + triangle[level][column];
    }
    
    return memo[level][column];
}
  • 3.动态规划

首先我们分析找到动态方程:
a.找重复性:
Problem(i,j) = min(subproblem(i+1,j),subprobelm(i+1,j+1) + a[i,j];
b. 定义状态数组,一维状态还是二维状态

c. DP方程:
F[i,j] = min(F[i+1,j],F[i+1,j+1]) + a[i,j];

代码如下,这道题还是比较有意思的,递推的时候先初始化最下面的,然后从下向上递推,那么可以从上往下递推么,我想了下,不是很方便,我们递推方程里面写的是[i,j] 它需要的是[i+1,j] 和 [i+1,j+1] 的值,从上往下的话好像是变成递推了,不知道我想的对不对,如果不对请指出哈,所以我们还是从下往上来遍历

int minimumTotal(int** triangle, int triangleSize, int* triangleColSize){
    int i,j;
    
    int **dp;
    dp = malloc(triangleSize * sizeof(int *));
    for (i = 0; i < triangleSize;i++) {
        dp[i] = malloc(sizeof(int) * triangleSize);
    }
    
    // 初始化最下面一排的dp数组
    for (i = 0;i < triangleColSize[triangleSize-1];i++) {
        dp[triangleSize-1][i] = triangle[triangleSize-1][i];
    }
    
    for (i = triangleSize-2;i>=0;i--) {
        for (j = 0; j < triangleColSize[i];j++) {
            int minNum;
            minNum = dp[i+1][j] < dp[i+1][j+1] ? dp[i+1][j] : dp[i+1][j+1];
            dp[i][j] = minNum + triangle[i][j];
        }
    }
    
    return dp[0][0];
}
  • 4.动态规划,我们可不可以一维数组替换二维数组存储呢,可以的哈,代码如下
int minimumTotal(int** triangle, int triangleSize, int* triangleColSize){
    int i,j;
    
    int *dp;
    dp = malloc(triangleSize * sizeof(int));
    
    // 初始化最下面一排的dp数组
    for (i = 0;i < triangleColSize[triangleSize-1];i++) {
        dp[i] = triangle[triangleSize-1][i];
    }
    
    for (i = triangleSize-2;i>=0;i--) {
        for (j = 0; j < triangleColSize[i];j++) {
            int minNum;
            minNum = dp[j] < dp[j+1] ? dp[j] : dp[j+1];
            dp[j] = minNum + triangle[i][j];
        }
    }
    
    return dp[0];
}

5.总结:
学习代码和解决问题的过程都是层层推进,一点点的积累的,中间是没有银弹的,只有我们一点点的付出和积累。这道题也一样,从分治超时,然后到记忆话数组存储解决超时的问题,再然后到二维数组的动态规划,最后到一维数组动态规划,都是一步步改进来的,所以无论生活还是工作,学习中都是没有捷径的。

posted @ 2020-03-01 16:57  Paualf  阅读(145)  评论(0编辑  收藏  举报