LeetCode第163场周赛(Weekly Contest 163)解题报告

这周的周赛,时间冲突,所以是后面补的题目。

第一题,直接按题目的意思进行模拟,时间复杂度过得去

第二题,二叉树,DFS

第三题,动态规划问题

第四题,BFS

详细题解如下。


1. 二维网格迁移(Shift 2d Grid)

            AC代码(C++)

2. 在受污染的二叉树中查找元素(Find Elements in A Contaminated Binary Tree)

            AC代码(C++)

3.可被三整除的最大和(Greatest Sum Divisible by Three)

            AC代码(C++)

4.推箱子(Minimum Moves to Move A Box to Their Target Location)

            AC代码( C++)

 


LeetCode第163场周赛地址:

https://leetcode-cn.com/contest/weekly-contest-163/


1. 二维网格迁移(Shift 2d Grid)

题目链接

https://leetcode-cn.com/problems/shift-2d-grid/

题意

给你一个 n 行 m 列的二维网格 grid 和一个整数 k。你需要将 grid 迁移 k 次。

每次「迁移」操作将会引发下述活动:

  • 位于 grid[i][j] 的元素将会移动到 grid[i][j + 1]。
  • 位于 grid[i][m - 1] 的元素将会移动到 grid[i + 1][0]。
  • 位于 grid[n - 1][m - 1] 的元素将会移动到 grid[0][0]。

请你返回 k 次迁移操作后最终得到的 二维网格。

示例 :有图示,所以具体的看链接

提示:

  • 1 <= grid.length <= 50
  • 1 <= grid[i].length <= 50
  • -1000 <= grid[i][j] <= 1000
  • 0 <= k <= 100

解题思路

根据题目的意思,还有示例,发现,每一次操作过后,数组发生了以下变化:

1、所有列都往后移动了一列,即 第 j 列变成了 j+1 列

2、所以最后一列变成第一列

3、在2的基础上,变成第一列后,第一列的数由行向下移动了一位

因此我们根据上述规律,对每一次操作,对数组进行以下变动:(n是行数,m是列数)

1、先保留最后一列的数(因为最后一列要变到第一列)

2、将 0 到 m-2列,往后移一列,变成 1 到 m-1列

3、将保留最后一列的数,向下移动一位的方式,放到第 0 列中。

时间复杂度是O(K*N*M),不会超时

AC代码(C++)

class Solution {
public:
    vector<vector<int>> shiftGrid(vector<vector<int>>& grid, int k) {
        int temp[55];
        int n = grid.size(), m = grid[0].size();
        while(k--)
        {
            for(int i = 0;i < n;i++) temp[i] = grid[i][m-1]; // 保留最后一列的数

            // 从 0 到 m-2 列的数,移动到 1到 m-1列上
            for(int j = m-1;j > 0;--j)
            {
                for(int i = 0;i < n;++i)
                    grid[i][j] = grid[i][j-1];
            }

            // 把原本最后一列的数,放回到第一列中
            grid[0][0] = temp[n-1];
            for(int i = 1;i < n;i++)
                grid[i][0] = temp[i-1];

        }
        return grid;
    }
};

2. 在受污染的二叉树中查找元素(Find Elements in A Contaminated Binary Tree)

题目链接

https://leetcode-cn.com/problems/find-elements-in-a-contaminated-binary-tree/

题意

给出一个满足下述规则的二叉树:

  1. root.val == 0
  2. 如果 treeNode.val == x 且 treeNode.left != null,那么 treeNode.left.val == 2 * x + 1
  3. 如果 treeNode.val == x 且 treeNode.right != null,那么 treeNode.right.val == 2 * x + 2

现在这个二叉树受到「污染」,所有的 treeNode.val 都变成了 -1。

请你先还原二叉树,然后实现 FindElements 类:

  • FindElements(TreeNode* root) 用受污染的二叉树初始化对象,你需要先把它还原。
  • bool find(int target) 判断目标值 target 是否存在于还原后的二叉树中并返回结果。

示例 :示例中有图示,因此具体的

提示:

  • TreeNode.val == -1
  • 二叉树的高度不超过 20
  • 节点的总数在 [1, 10^4] 之间
  • 调用 find() 的总次数在 [1, 10^4] 之间
  • 0 <= target <= 10^6

 

解题思路

DFS + set

题目形容的很复杂,其实就是一棵二叉树,左右子节点的值根据父节点的值来计算,根节点的起始值为 0。

先利用DFS,从根节点深搜,记录父节点的val,然后得到左右子节点的val。

为了可以 find,我们利用一个集合存储所有出现的 val,然后每一个find就查找以下对应值有没有在这个集合中。

AC代码(C++)

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class FindElements {
public:
    unordered_set<int> s;
    FindElements(TreeNode* root) {
        if(root) dfs(root, 0);
    }
    
    bool find(int target) {
        return s.count(target);
    }
    
    void dfs(TreeNode* root, int val)
    {
        s.insert(val);
        root->val = val;
        if(root->left) dfs(root->left, 2*val+1);
        if(root->right) dfs(root->right, 2*val+2);
    }

};

/**
 * Your FindElements object will be instantiated and called as such:
 * FindElements* obj = new FindElements(root);
 * bool param_1 = obj->find(target);
 */

3.可被三整除的最大和(Greatest Sum Divisible by Three)

题目链接

https://leetcode-cn.com/problems/greatest-sum-divisible-by-three/

题意

给你一个整数数组 nums,请你找出并返回能被三整除的元素最大和。

示例 1:

输入:nums = [3,6,5,1,8]
输出:18
解释:选出数字 3, 6, 1 和 8,它们的和是 18(可被 3 整除的最大和)。

示例 2:

输入:nums = [4]
输出:0
解释:4 不能被 3 整除,所以无法选出数字,返回 0。

示例 3:

输入:nums = [1,2,3,4,4]
输出:12
解释:选出数字 1, 3, 4 以及 4,它们的和是 12(可被 3 整除的最大和)。

提示:

  • 1 <= nums.length <= 4 * 10^4
  • 1 <= nums[i] <= 10^4

解题分析

根据题意,这个应该是一个动态规划的问题,假设有 n 个数。

设 dp[n][3],其中 dp[ i ][ j ] 表示,前 i 个数中,余数为 j 的元素最大和。

状态转移方程:

对于当前处理的这个数,设为 num,对于不同的余数 0,1,2(即 j )

那么 dp[i+1][  (j+num)%3 ],考虑这个新的数进来后,余数变化的时候,对于余数为 (j+num)%3 应该是

1、要么没有加入这个数,所以是前 i 中的 dp[i][  (j+num)%3 ]

2、要么就是考虑加入了这个数 num,那么就是 dp[i][j] + num,因为对于新的余数(j+num)%3 的最大和而言,应该是 余数为 j 前i 个数的最大和 加上了新的这个数 num,组成新的 对应余数 最大和

初始条件,dp[0][0] = 0,当没有数的时候,余数为0的最大和肯定是0,但是没有数的时候,余数为1,和2 的最大和是不存在的,也就是设为最小值,为了后面更新最大值来处理的。

上面用到的是,二维数组来记录,为了节省内存,我们可以用两个一维数组,一个一维数组每次更新,最后把值保留给另一个数组,这样子就实现了和上面用二维数组相同功能,但节省了内存。

AC代码(C++)

#define INF 0x3f3f3f3f

class Solution {
public:
    int maxSumDivThree(vector<int>& nums) {
        int dp[3], g[3];
        dp[0] = 0;
        dp[1] = dp[2] = -INF;

        for(auto num : nums)
        {
            for(int i = 0;i < 3;i++)
            {
                g[(i + num)%3] = max(dp[(i + num)%3], dp[i] + num);
            }

            for(int i = 0;i < 3;i++) 
                dp[i] = g[i];
        }

        return dp[0];
    }
};

 


4.推箱子(Minimum Moves to Move A Box to Their Target Location)

题目链接

https://leetcode-cn.com/problems/minimum-moves-to-move-a-box-to-their-target-location/

题意

「推箱子」是一款风靡全球的益智小游戏,玩家需要将箱子推到仓库中的目标位置。

游戏地图用大小为 n * m 的网格 grid 表示,其中每个元素可以是墙、地板或者是箱子。

现在你将作为玩家参与游戏,按规则将箱子 'B' 移动到目标位置 'T' :

  • 玩家用字符 'S' 表示,只要他在地板上,就可以在网格中向上、下、左、右四个方向移动。
  • 地板用字符 '.' 表示,意味着可以自由行走。
  • 墙用字符 '#' 表示,意味着障碍物,不能通行。 
  • 箱子仅有一个,用字符 'B' 表示。相应地,网格上有一个目标位置 'T'。
  • 玩家需要站在箱子旁边,然后沿着箱子的方向进行移动,此时箱子会被移动到相邻的地板单元格。记作一次「推动」。
  • 玩家无法越过箱子。

返回将箱子推到目标位置的最小 推动 次数,如果无法做到,请返回 -1。

示例 :示例中有图示,具体看题目链接

提示:

  • 1 <= grid.length <= 20
  • 1 <= grid[i].length <= 20
  • grid 仅包含字符 '.', '#',  'S' , 'T', 以及 'B'。
  • grid 中 'S', 'B' 和 'T' 各只能出现一个。

 

解题分析

BFS的题,同时记录人和箱子的位置。

由于本人对BFS不是很会,因此参考了周赛排名第11的 wnjxyk 大佬的代码。

根据代码的意思,就是要同时记录人和箱子的位置,遍历人BFS,判断是否可以人移动到下一个位置(四个方向),如果可以人就移动

当人与箱子位置重合,说明人推动了箱子,那么箱子同人移动的方向,箱子也移动,此时也要判断箱子是否能移动到下一个位置。

剩下就是基本的额BFS操作。

AC代码( C++)

struct Node{
    Node(){}
    Node(int x0, int y0, int bx0, int by0){
        x = x0; y = y0;
        bx = bx0; by = by0;
    }
    int x, y;
    int bx, by;
    friend bool operator < (const Node a, const Node b){
        if (a.x != b.x) return a.x < b.x;
        if (a.y != b.y) return a.y < b.y;
        if (a.bx != b.bx) return a.bx < b.bx;
        return a.by < b.by;
    }
};

int dx[4] = {0, -1, 0, 1};
int dy[4] = {1, 0, -1, 0};

queue<Node> que;
map<Node, bool> inQue;
map<Node, int> dist;

class Solution {
public:
    
    
    int minPushBox(vector<vector<char>>& grid) {
        int n = grid.size(), m = grid[0].size();
        
        while(!que.empty()) que.pop();
        dist.clear();
        inQue.clear();
        
        int ans = -1;
        int px, py, bx, by, tx, ty;
        for (int i = 0; i < n; ++i){
            for(int j = 0; j < m; ++j){
                if (grid[i][j] == 'S') px = i, py = j, grid[i][j] = '.';
                if (grid[i][j] == 'B') bx = i, by = j, grid[i][j] = '.';
                if (grid[i][j] == 'T') tx = i, ty = j, grid[i][j] = '.';
            }
        }
        
        // printf("%d %d\n", px, py);
        // printf("%d %d\n", bx, by);
        // printf("%d %d\n", tx, ty);
        
        que.push(Node(px, py, bx, by));
        inQue[Node(px, py, bx, by)] = true;
        dist[Node(px, py, bx, by)] = 0;
        
        while(!que.empty()){
            int px = que.front().x, py = que.front().y, bx = que.front().bx, by = que.front().by;
            int cur = dist[que.front()], delta = 0;
            
            inQue[que.front()] = false;
            que.pop();
            
            
            
            if (bx == tx && by == ty){
                if (ans == -1 || ans > cur) ans = cur;
                continue;
            }
            
            for (int k = 0; k < 4; ++k){
                delta = 0;
                int nx = px + dx[k], ny = py + dy[k];
                int nbx = bx, nby = by;
                
                if (nx >= n || nx < 0 || ny >= m || ny < 0) continue;
                if (grid[nx][ny] == '#') continue;
                
                if (nx == bx && ny == by){
                    nbx = bx + dx[k];
                    nby = by + dy[k];
                    delta = 1;
                    if (nbx >= n || nbx < 0 || nby >= m || nby < 0) continue;
                    if (grid[nbx][nby] == '#') continue;
                }
                
                Node now = Node(nx, ny, nbx, nby);
                if (dist.count(now) == 0 || dist[now] > cur + delta){
                    
                    dist[now] = cur + delta;
                    // if (delta == 1) printf("> %d %d %d %d => %d\n", nx, ny, nbx, nby, dist[now] );
                    // if (nbx == 3 && nby == 3) printf(">* %d %d %d %d (%d %d %d %d)=> %d %d\n", nx, ny, nbx, nby, px, py, bx,by,delta,dist[now] );
                    if (!inQue[now]){
                        inQue[now] = true;
                        que.push(now);
                    }
                }
            }
        }
        
        return ans;
    }
};

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值