二维矩阵中搜索最长波折路径(字节)

82 阅读3分钟

题意

给定一个 N x N 的二维数组,期望能找到一条波折路径,

路径起点是能从任何一个位置开始,终点也可以是任何一个位置。每个位置只能访问一次

波折路径是指相邻元素严格交替增减,每一个值相比前值要么是大于,要么是小于,而且是交替的,
上一个值比前一个值小,那么当前选择的值就应该比上一个值大。

eg:
input[1 0 1 0]
[1 0 1 1]
[1 0 1 0]
[1 0 1 1]

output7
最长波折路径为 1 0 1 0 1 0 1

解法一:二维dp

  • 状态定义:利用2个二维数组记录每个位置的最长波折路径长度
    1. up[i][j]:以位置(i,j)结尾且最后一步是上升(当前元素比前一个大)的最长路径长度
    2. down[i][j]:以位置(i,j)结尾且最后一步是下降(当前元素比前一个小)的最长路径长度
  • 状态转移:对于每个位置(i,j),检查其四个方向的相邻位置:
    • 如果相邻位置的值比当前值小,则可以从该相邻位置的up状态转移到当前位置的down状态
    • 如果相邻位置的值比当前值大,则可以从该相邻位置的down状态转移到当前位置的up状态
func main() {
    matrix := [][]int{
       {1, 0, 1, 0},
       {1, 0, 1, 1},
       {1, 0, 1, 0},
       {1, 0, 1, 1},
    }

    fmt.Println(longestWavyPath(matrix)) // 输出7
}

func longestWavyPath(matrix [][]int) int {
    if len(matrix) == 0 || len(matrix[0]) == 0 {
       return 0
    }

    rows := len(matrix)
    cols := len(matrix[0])

    // 初始化 up和 down二维数组,也充当搜索过程的备忘录(避免重复走进同个节点)
    up := make([][]int, rows)
    for i := range up {
       up[i] = make([]int, cols)
    }
    down := make([][]int, rows)
    for i := range down {
       down[i] = make([]int, cols)
    }

    var res int
    // 遍历每个位置进行DFS
    for i := 0; i < rows; i++ {
       for j := 0; j < cols; j++ {
          dfs(matrix, i, j, up, down, &res)
       }
    }

    return res
}

func dfs(matrix [][]int, i int, j int, up [][]int, down [][]int, maxLen *int) {
    // 如果当前位置(i, j)已经计算过,直接返回
    if up[i][j] > 0 || down[i][j] > 0 {
       return
    }
    // 初始化当前位置的up和down为1(至少包含自身)
    up[i][j] = 1
    down[i][j] = 1
    // 遍历四个方向
    dirs := [][]int{{-1, 0}, {1, 0}, {0, -1}, {0, 1}} // 方向数组
    for _, dir := range dirs {
       x := i + dir[0]
       y := j + dir[1]

       // 检查边界
       if x < 0 || x >= len(matrix) || y < 0 || y >= len(matrix[0]) {
          continue
       }

       // 递归处理相邻位置(x, y)
       dfs(matrix, x, y, up, down, maxLen)

       // 更新当前位置(i, j)的up和down值,仅在递增或递减时才需要更新
       if matrix[x][y] < matrix[i][j] { // 相邻位置 (x,y) 小于当前值 matrix[i][j]
          // 若考虑(x,y)是最长路径的前一个元素,
          // 则当前这一步是上升的(从(x,y)到(i,j)是递增的),那么前一步必须是下降的
          // 那么(i,j)的最长up路径长度可能为(x,y)的最长down路径长度,再加上当前这一步
          up[i][j] = max(up[i][j], down[x][y]+1)
       } else if matrix[x][y] > matrix[i][j] { // 相邻位置 (x,y) 大于当前值 matrix[i][j]
          // 若考虑(x,y)是最长路径的前一个元素,
          // 则当前这一步是下降的(从(x,y)到(i,j)是递减的),那么前一步必须是上升的
          // 那么(i,j)的最长down路径长度可能为(x,y)的最长up路径长度,再加上当前这一步
          down[i][j] = max(down[i][j], up[x][y]+1)
       }
       // 到达(i,j)的最长波折路径,在up[i][j], down[i][j]中取较大值
       currentMax := max(up[i][j], down[i][j])
       // 更新全局最大长度
       if currentMax > *maxLen {
          *maxLen = currentMax
       }
    }
}

func max(a, b int) int {
    if a > b {
       return a
    }
    return b
}