contest-250

126 阅读1分钟

1.可以输入的最大单词数 (略)

2.新增的最少台阶数 (贪心+模拟)

正常模拟就可以过,这里面有个小小的坑,当两个台阶相差过大时,需要一次性计算出所有的台阶数,如果模拟会TLE

func addRungs(rungs []int, dist int) int {
   cur := 0
   res := 0
   for i := 0 ; i < len(rungs) ; {
      if cur + dist < rungs[i] {
         diff := rungs[i] - cur
         t := diff / dist
         if diff % dist == 0 {
            t--
         }
         cur = cur + dist * t
         res += t
      } else {
         cur = rungs[i]
         i++
      }
   }
   return res
}

3.扣分后的最大得分 (dp+拆项)

首先很容易分析出来需要dp来做状态定义为dp[i][j]:选i,j位置时的所获得的最大得分 dp[i][j] = max(dp[i - 1][a] - |a - j|) + point[i][j], 朴素的办法是每次计算一个格子时,都去遍历上一行所有的格子以得到最大值,则最终的答案就是最后一行中的最大值,但是复杂度为O(mn^2)必TLE,需要优化。 这里考虑将绝对值符号去掉以a<=j为例:

原式变为:dp[i][j] = max(dp[i - 1][a] + a) + point[i][j] - j 这样一来,针对上一行计算出的结果,先从左到右统计每一个位置左边的最大值,右边同理,这样当前在计算的时候将左右的最大值带入公式中,就能在O(1)的时间求出得分的最大值了。时间复杂度O(nm) 一般裸dp过不了,可能就需要对其进行优化常见的有拆项讨论,优先队列,单调队列,数学推导等等

func maxPoints(points [][]int) int64 {
   n := len(points)
   m := len(points[0])
   dp := make([][]int64, n)
   left := make([]int, m)
   right := make([]int, m)
   for i := 0 ; i < len(dp) ; i++ {
      dp[i] = make([]int64, m)
   }
   for i := 0 ; i < m ; i++ {
      dp[0][i] = int64(points[0][i])
   }
   for i := 1 ; i < len(dp) ; i++ {
      left[0] = int(dp[i - 1][0])
      for k := 1 ; k < m ; k++ {
         left[k] = max(left[k - 1], int(dp[i - 1][k]) + k)
      }
      right[m - 1] = int(dp[i - 1][m - 1]) - (m - 1)
      for k := m - 2 ; k >= 0 ; k-- {
         right[k] = max(right[k + 1], int(dp[i - 1][k]) - k)
      }
      for j := 0 ; j < len(dp[i]) ; j++ {
         dp[i][j] = int64(max(points[i][j] - j + left[j], points[i][j] + j + right[j]))
      }
   }
   var res int64
   for i := 0 ; i < m ; i++ {
      if dp[len(dp) - 1][i] > res {
         res = dp[len(dp) - 1][i]
      }
   }
   return res
}