【周赛】第157场-2019-10-6

本文解析了四道LeetCode算法题,包括数组、哈希表、DP、BFS的应用,如PlaywithChips、LongestArithmeticSubsequenceofGivenDifference、PathwithMaximumGold及CountVowelsPermutation,提供了详细的解题思路和代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录

 

备注

1-Play with Chips-easy。数组

2-Longest Arithmetic Subsequence of Given Difference-medium。哈希表、DP

3-Path with Maximum Gold-medium。BFS

4-Count Vowels Permutation-hard。DP、BFS


备注

  • Q1。对问题做转化是一种需要训练的建模思想。比如第一题,把cost转化成求奇偶数的个数,这里选定哪个位置最小已经不重要了,重要的是抽象成选定位置是奇还是偶,对应的cost
  • Q2。由于等差数列的性质,运用DP,这里只需知道上一个数对应的最长长度。注意思考与LIS问题的异同
  • Q3。网格类寻宝的问题可以有很多种游戏设定,分为2类,一类是到达终点的路径优化,一类是沿途收益优化,还有就是两者的结合了,这里是第二类。所以遍历所有起始点的可能性,找出对应的收益求最值,在找的过程中,单一路径的求和记得用局部变量传参
  • Q4。披着拓扑排序影子的DP问题,这里知道图关系(可以建图,也可以写在逻辑里),找到上一时刻和下一时刻的变换关系。原先是想在BFS过程中把整颗搜索树的某一层保留在一个队列里,开销很大,这里其实只需保留每棵子树的叶子结点个数,因为每个叶子节点分别对应的aeiou,而后续生长关系又是确定的,所以只需保留节点个数,在下一次迭代时用他们之间的连接关系更新即可。

1-Play with Chips-easy。数组

There are some chips, and the i-th chip is at position chips[i].

You can perform any of the two following types of moves any number of times (possibly zero) on any chip:

  • Move the i-th chip by 2 units to the left or to the right with a cost of 0.
  • Move the i-th chip by 1 unit to the left or to the right with a cost of 1.

There can be two or more chips at the same position initially.

Return the minimum cost needed to move all the chips to the same position (any position).

Example 1:

Input: chips = [1,2,3]
Output: 1
Explanation: Second chip will be moved to positon 3 with cost 1. First chip will be moved to position 3 with cost 0. Total cost is 1.

Example 2:

Input: chips = [2,2,2,3,3]
Output: 2
Explanation: Both fourth and fifth chip will be moved to position two with cost 1. Total minimum cost will be 2.

Constraints:

  • 1 <= chips.length <= 100
  • 1 <= chips[i] <= 10^9

乍看很难,看chips长度,暴力应该能接受,一开始看测试用例理解错了,以为下标就是在数轴上的位置,其实元素值才代表在数轴上的位置.

优化的解法是统计数组里的奇偶数的个数,奇数个数代表选定偶数坐标为终点时的cost,因为此时偶数之间transfer是没有cost的。偶数vice versa。巧妙

# 暴力
class Solution:
    def minCostToMoveChips(self, chips: List[int]) -> int:
        if not chips:
            return 0
        n = len(chips)
        res = n+1
        for i in chips:
            cur = 0
            for j in chips:
                if abs(i-j) & 1:
                    cur += 1
            res = min(res, cur)
        return res

# 优化解法
class Solution:
    def minCostToMoveChips(self, chips: List[int]) -> int:
        if not chips:
            return 0
        n = len(chips)
        l = r = 0
        for i in chips:
            if i & 1:
                l += 1
            else:
                r += 1
        return min(l, r)

2-Longest Arithmetic Subsequence of Given Difference-medium。哈希表、DP

Given an integer array arr and an integer difference, return the length of the longest subsequence in arr which is an arithmetic sequence such that the difference between adjacent elements in the subsequence equals difference.

 

Example 1:

Input: arr = [1,2,3,4], difference = 1
Output: 4
Explanation: The longest arithmetic subsequence is [1,2,3,4].

Example 2:

Input: arr = [1,3,5,7], difference = 1
Output: 1
Explanation: The longest arithmetic subsequence is any single element.

Example 3:

Input: arr = [1,5,7,8,5,3,4,2,1], difference = -2
Output: 4
Explanation: The longest arithmetic subsequence is [7,5,3,1].

Constraints:

  • 1 <= arr.length <= 10^5
  • -10^4 <= arr[i], difference <= 10^4

这道题一直想着用LIS,LIS的O(n^2)方法超时,O(nlogn)没想出来怎么改写。看大神的解法是用字典记录每个元素,上一个valid的值对应的长度,很妙,因为这里是等差的,所以可以顺利索引到上一个值是什么,但LIS问题只要求递增,不要求数与数的关系才会导致要一个个遍历之前的数的,此处不用,很巧妙。

class Solution:
    def longestSubsequence(self, arr: List[int], difference: int) -> int:
        if not arr:
            return 0
        n = len(arr)
        dic = {}
        res = 1
        for n in arr:
            dic[n] = dic.get(n-difference, 0) + 1
            res = max(res, dic[n])
        return res

3-Path with Maximum Gold-medium。BFS

In a gold mine grid of size m * n, each cell in this mine has an integer representing the amount of gold in that cell, 0 if it is empty.

Return the maximum amount of gold you can collect under the conditions:

  • Every time you are located in a cell you will collect all the gold in that cell.
  • From your position you can walk one step to the left, right, up or down.
  • You can't visit the same cell more than once.
  • Never visit a cell with 0 gold.
  • You can start and stop collecting gold from any position in the grid that has some gold.

Example 1:

Input: grid = [[0,6,0],[5,8,7],[0,9,0]]
Output: 24
Explanation:
[[0,6,0],
 [5,8,7],
 [0,9,0]]
Path to get the maximum gold, 9 -> 8 -> 7.

Example 2:

Input: grid = [[1,0,7],[2,0,6],[3,4,5],[0,3,0],[9,0,20]]
Output: 28
Explanation:
[[1,0,7],
 [2,0,6],
 [3,4,5],
 [0,3,0],
 [9,0,20]]
Path to get the maximum gold, 1 -> 2 -> 3 -> 4 -> 5 -> 6 -> 7.

Constraints:

  • 1 <= grid.length, grid[i].length <= 15
  • 0 <= grid[i][j] <= 100
  • There are at most 25 cells containing gold.

比较典型的BFS题,和以往不同的是这里1是没有起始点,从任何一个非0点开始都可以也就意味着有多重拾金子的路径,2是没有设定目标点也就是没有结束判定,所以要自己设定结束判定,我设的是当周围无路可走时就把收集到的gold求和,再判断是否为截至目前的最大值。

但做法1还是太耗空间了,这里用grid自身标记的方法,而且也不必记录沿途收集过的金币,用一个局部变量cur记录即可,传参进去,这样不同路径的cur是互不干扰的,所以这里cur一定不能用全局变量,如果用的话相当于走过的地方全被记上了

# DIY做法1
class Solution:
    def getMaximumGold(self, grid: List[List[int]]) -> int:
        if not grid:
            return 0
        m = len(grid)
        n = len(grid[0])
        self.res = 0
        around = [[-1, 0], [1, 0], [0, -1], [0, 1]]
        visited = [[False for _ in range(n)] for _ in range(m)]
        for i in range(m):
            for j in range(n):
                if grid[i][j] > 0:
                    visited[i][j] = True
                    self.dfs(grid, around, i, j, m, n, [grid[i][j]], visited)
                    visited[i][j] = False
        return self.res
    
    def dfs(self, grid, around, i, j, m, n, path, visited):
        flag = False
        for d in around:
            n_i = i + d[0]
            n_j = j + d[1]
            if n_i < 0 or n_i >= m or n_j < 0 or n_j >= n or grid[n_i][n_j] <= 0 or visited[n_i][n_j]:
                continue
            flag = True
            path.append(grid[n_i][n_j])
            visited[n_i][n_j] = True
            self.dfs(grid, around, n_i, n_j, m, n, path, visited)
            visited[n_i][n_j] = False
            path.pop()
        if not flag:
            self.res = max(self.res, sum(path))
        return

# 优化后的做法2
class Solution:
    def getMaximumGold(self, grid: List[List[int]]) -> int:
        if not grid:
            return 0
        m = len(grid)
        n = len(grid[0])
        self.res = 0
        around = [[-1, 0], [1, 0], [0, -1], [0, 1]]
        for i in range(m):
            for j in range(n):
                if grid[i][j] > 0:
                    cur = 0
                    self.dfs(grid, around, i, j, m, n, cur)
        return self.res
    
    def dfs(self, grid, around, i, j, m, n, cur):
        cur += grid[i][j]
        grid[i][j] = -grid[i][j] # 这是个好办法,比置0更方便
        self.res = max(self.res, cur)
        for d in around:
            n_i = i + d[0]
            n_j = j + d[1]
            if n_i < 0 or n_i >= m or n_j < 0 or n_j >= n or grid[n_i][n_j] <= 0:
                continue
            self.dfs(grid, around, n_i, n_j, m, n, cur)
        grid[i][j] = -grid[i][j]
        return

4-Count Vowels Permutation-hard。DP、BFS

Given an integer n, your task is to count how many strings of length n can be formed under the following rules:

  • Each character is a lower case vowel ('a''e''i''o''u')
  • Each vowel 'a' may only be followed by an 'e'.
  • Each vowel 'e' may only be followed by an 'a' or an 'i'.
  • Each vowel 'i' may not be followed by another 'i'.
  • Each vowel 'o' may only be followed by an 'i' or a 'u'.
  • Each vowel 'u' may only be followed by an 'a'.

Since the answer may be too large, return it modulo 10^9 + 7.

Example 1:

Input: n = 1
Output: 5
Explanation: All possible strings are: "a", "e", "i" , "o" and "u".

Example 2:

Input: n = 2
Output: 10
Explanation: All possible strings are: "ae", "ea", "ei", "ia", "ie", "io", "iu", "oi", "ou" and "ua".

Example 3: 

Input: n = 5
Output: 68

Constraints:

  • 1 <= n <= 2 * 10^4

这道题我理解是存储好图的关系,在再用BFS去生长所有路径,最后子节点的个数就是结果,但到n=144之后的TLE了。 

 估计是维护一个队列,进出很耗开销,尝试把具体字符换成对应的数。看看大神的做法,简洁精彩

class Solution:
    def countVowelPermutation(self, n: int) -> int:
        if n < 1:
            return 0
        mod = 10**9 + 7
        a, e, i, o, u = 1, 1, 1, 1, 1
        for _ in range(n-1):
            a_ = e + i + u
            e_ = a + i
            i_ = e + o
            o_ = i
            u_ = o + i
            a, e, i, o, u = a_, e_, i_, o_, u_
        res = (a%mod + e%mod + i%mod + o%mod + u%mod) % mod
        return res

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值