LeetCode Notes: Weekly Contest 215 Contest Record

0. Summary after the game

This time, the ranking of the competition is still reasonable. The domestic 327, the world 830, are finally in the top 10%, but the process is really huh, 4 times wrong, 3 of which are extremely stupid mistakes, and finally I gave up half an hour earlier, and the last question really had no thoughts at all, so the overall feeling was extremely bad.

Hey, when will I get all 4 questions steadily? . .

Come on, boy.

1. Topic One

The link to the question given for question one is as follows:

1. Problem-solving ideas

In essence, the difficulty of this question lies in the reading of the question. Once you understand the question, you can solve it directly. If you don't understand the question, then hehe. . .

In fact, in essence, this question is to give an out-of-order sequence, and then give an initial position pointer ptr of 1, and then fill in the out-of-order sequence one by one, when the filled element happens to be the element pointed to by the position of ptr When, move ptr backward to the next position that has not been filled in the element, and return these values.

2. Code implementation

Given the python code implementation is as follows:

class OrderedStream:

    def __init__(self, n: int):
        self.val = ["" for _ in range(n+1)]
        self.flag = 1
        self.n = n


    def insert(self, id: int, value: str) -> List[str]:
        self.val[id] = value
        res = []
        if id == self.flag:
            while self.flag <= self.n and self.val[self.flag] != "":
                res.append(self.val[self.flag])
                self.flag += 1
        return res

After submitting the code evaluation, it took 220ms and took up 14.7MB of memory.

The current optimal code implementation takes 216ms, and its implementation is slightly different from ours. We keep the elements, use a list to store the elements, and they directly use a storage element.

However, in essence, there is not much difference, so I won't do more.

2. Topic 2

The link to the test questions for topic two is as follows:

1. Problem-solving ideas

The essence of this question lies in the analysis of the question. Although I overturned this question carelessly, after analyzing the conditions of the question, the question is actually very simple:

  1. Since the positions of the two elements can be exchanged at any time, the sequence is independent of the order;
  2. Since any two element types can be interchanged as a whole, the sequence is independent of letter types.

Therefore, as long as the following two conditions are met, the two strings (denoted as s1 and s2) are close:

  1. Two strings have the same kind of letters, that is, if a letter appears in s1, it must appear in s2, and vice versa;
  2. The number set of each letter in s1 is exactly the same as the number set of letters in s2, that is, if there are three kinds of characters in s1, and the numbers are respectively [a,b,c], then the number of three characters in s2 must be the same [a,b,c].

2. Code implementation

The python code is given as follows:

class Solution:
    def closeStrings(self, word1: str, word2: str) -> bool:
        s1 = Counter(word1)
        s2 = Counter(word2)
        return sorted(s1.keys()) == sorted(s2.keys()) and sorted(s1.values()) == sorted(s2.values())

After submitting the code evaluation, it took 128ms and took up 14.6MB of memory. Belongs to the current optimal code implementation.

3. Topic Three

The link to the test questions for topic three is as follows:

1. Problem-solving ideas

After getting the title of this question, my first thought was to use a dp algorithm to solve it violently, and then it timed out. . .

Later, I carefully considered this topic. It feels similar to the two-way search algorithm, because the elements are greater than 0. Therefore, we first give that all elements come from the right side to reach x, and then see how many elements are needed, and then Each time an element is added to the left, and then the element is deleted from the right to make it reach x again.

Repeat the above process until all the elements are from the left.

2. Code implementation

The python code is given as follows:

class Solution:
    def minOperations(self, nums: List[int], x: int) -> int:
        tot = sum(nums)
        if tot < x:
            return -1
        elif tot == x:
            return len(nums)
        
        n = len(nums)
        l, r = 0, n-1
        s = 0
        used = 0
        while s < x:
            s += nums[r]
            r -= 1
            used += 1
        res = -1 if s != x else used
        while r < n-1 and l <= r :
            used -= 1
            r += 1
            s -= nums[r]
            while s < x and l <= r:
                s += nums[l]
                l += 1
                used += 1
            if s == x:
                res = used if res == -1 else min(res, used)
        return res

After submitting the code for evaluation, it took 1164ms and took up 28.9MB of memory.

The current optimal code implementation takes 652ms. Looking at his algorithm, it is not much different from ours in essence, but he uses a dictionary to save the previous sum, thereby optimizing efficiency.

Interested readers can take a look at it by themselves, I won't go into it too much here.

4. Topic Four

The link to the test question for question four is as follows:

1. Problem-solving ideas

We have no ideas for this question in the competition. After the competition, referring to the solution of the awice boss, a dynamic planning idea is given as follows:

  • Each time you fill a row, check the score change after each legal filling, and know that all rows are filled.

2. Code implementation

The python code is given as follows:

class Solution:
    def getMaxGridHappiness(self, m: int, n: int, introvertsCount: int, extrovertsCount: int) -> int:        
        @lru_cache(None)
        def dp(r, intro, extro, prev):
            if r >= m:
                return 0
            ans = 0
            for row in itertools.product(range(3), repeat=n):
                _intro = row.count(1)
                _extro = row.count(2)
                if _intro > intro or _extro > extro:
                    continue
                score = 0
                for i in range(n):
                    if row[i] == 0:
                        continue
                    elif row[i] == 1:
                        score += 120
                        if i-1 >= 0 and row[i-1] != 0:
                            score -= 30
                        if i+1 < n and row[i+1] != 0:
                            score -= 30
                        if prev[i] == 1:
                            score -= 60
                        elif prev[i] == 2:
                            score -= 10
                    else:
                        score += 40
                        if i-1 >= 0 and row[i-1] != 0:
                            score += 20
                        if i+1 < n and row[i+1] != 0:
                            score += 20
                        if prev[i] == 1:
                            score -= 10
                        elif prev[i] == 2:
                            score += 40
                score += dp(r+1, intro - _intro, extro - _extro, tuple(row))
                ans = max(ans, score)
            return ans

        return dp(0, introvertsCount, extrovertsCount, tuple([0] * n))

However, the above code has a timeout problem. Therefore, we have to forcefully add two special treatments for complex situations in exactly the way of the awice boss, so that it will not time out:

  1. The number of columns is less than the number of rows, and there are fewer considerations for each row at this time;
  2. In the case of 5*5, everyone can be placed in without conflict, so it can be dealt with directly externally;

Therefore, the revised code gets:

class Solution:
    def getMaxGridHappiness(self, m: int, n: int, introvertsCount: int, extrovertsCount: int) -> int:
        if m < n:
            return self.getMaxGridHappiness(n, m, introvertsCount, extrovertsCount)
        
        if n >= 5:
            bonus = {
    
    0:0, 1:0, 2:40, 3:80, 4:160, 5:200, 6:280}
            return 120 * introvertsCount + 40 * extrovertsCount + bonus[extrovertsCount]
        
        @lru_cache(None)
        def dp(r, intro, extro, prev):
            if r >= m:
                return 0
            ans = 0
            for row in itertools.product(range(3), repeat=n):
                _intro = row.count(1)
                _extro = row.count(2)
                if _intro > intro or _extro > extro:
                    continue
                score = 0
                for i in range(n):
                    if row[i] == 0:
                        continue
                    elif row[i] == 1:
                        score += 120
                        if i-1 >= 0 and row[i-1] != 0:
                            score -= 30
                        if i+1 < n and row[i+1] != 0:
                            score -= 30
                        if prev[i] == 1:
                            score -= 60
                        elif prev[i] == 2:
                            score -= 10
                    else:
                        score += 40
                        if i-1 >= 0 and row[i-1] != 0:
                            score += 20
                        if i+1 < n and row[i+1] != 0:
                            score += 20
                        if prev[i] == 1:
                            score -= 10
                        elif prev[i] == 2:
                            score += 40
                score += dp(r+1, intro - _intro, extro - _extro, tuple(row))
                ans = max(ans, score)
            return ans

        return dp(0, introvertsCount, extrovertsCount, tuple([0] * n))

Submit the code for evaluation and get: It takes 4524ms and takes up 24.3MB of memory.

The current optimal code implementation takes only 604ms, so the code needs to be further optimized.

4. Algorithm optimization

The most cumbersome part of the above code is that all possible arrangements are examined for each row, but in fact, due to the limitations of introCounts and extroCounts, only a small part of them will be legal, so a lot of calculation redundancy will be generated. .

Looking at the current optimal solution, he directly adopted the dfs calculation method to implement the code, that is, considering the possible filling of each point.

However, there are two points to note here:

  1. When all the remaining people get the highest score and cannot reach the highest score in history, the dfs process can be terminated directly;
  2. Each point should be used as much as possible, that is, only when at least one of the previous point and the previous point of the current point is not empty can the current point be left blank, otherwise, a person must be filled in.

Below, we imitate it and give our own code implementation.

class Solution:
    def getMaxGridHappiness(self, m: int, n: int, introvertsCount: int, extrovertsCount: int) -> int:
        grid = [[0 for _ in range(n)] for _ in range(m)]
        score = 0
        
        def get_score(r, c):
            nonlocal grid
            if grid[r][c] == 1:
                score = 120
                if r-1 >= 0:
                    if grid[r-1][c] == 1:
                        score -= 60
                    elif grid[r-1][c] == 2:
                        score -= 10
                if c-1 >= 0:
                    if grid[r][c-1] == 1:
                        score -= 60
                    elif grid[r][c-1] == 2:
                        score -= 10
            else:
                score = 40
                if r-1 >= 0:
                    if grid[r-1][c] == 1:
                        score -= 10
                    elif grid[r-1][c] == 2:
                        score += 40
                if c-1 >= 0:
                    if grid[r][c-1] == 1:
                        score -= 10
                    elif grid[r][c-1] == 2:
                        score += 40
            return score
        
        def dfs(r, c, intro, extro, _score):
            nonlocal grid, score
            if c >= n:
                dfs(r+1, 0, intro, extro, _score)
                return
            if r >= m or (intro == 0 and extro == 0):
                score = max(_score, score)
                return
            if _score + 120 * (intro + extro) < score:
                return
            if intro > 0:
                grid[r][c] = 1
                dfs(r, c+1, intro-1, extro, _score + get_score(r,c))
                grid[r][c] = 0
            if extro > 0:
                grid[r][c] = 2
                dfs(r, c+1, intro, extro-1, _score + get_score(r,c))
                grid[r][c] = 0
            if (r > 0 and grid[r-1][c] != 0) or (c > 0 and grid[r][c-1] != 0):
                dfs(r, c+1, intro, extro, _score)
            return
        
        dfs(0, 0, introvertsCount, extrovertsCount, 0)
        return score

After submitting the code evaluation, it took 916ms and took up 14.2MB of memory.

Guess you like

Origin blog.csdn.net/codename_cys/article/details/109828853