LeetCode Contest 163

5263. 二维网格迁移

给你一个 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 = [[1,2,3],[4,5,6],[7,8,9]], k = 1
输出:[[9,1,2],[3,4,5],[6,7,8]]

示例 2:
输入:grid = [[3,8,1,9],[19,7,2,5],[4,6,11,10],[12,0,21,13]], k = 4
输出:[[12,0,21,13],[3,8,1,9],[19,7,2,5],[4,6,11,10]]
示例 3:

输入:grid = [[1,2,3],[4,5,6],[7,8,9]], k = 9
输出:[[1,2,3],[4,5,6],[7,8,9]]

提示:

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

思路

首先把kn * m求模作为新的k,然后计算kn * m的最小公约数g。之后,将n * m个元素分成g组,每组n * m / g个元素,分别内部循环。时间复杂度O(n * m).

代码

class Solution {
public:
    int gcd(int a, int b) {
        while (b != 0) {
            int tmp = b;
            b = a % b;
            a = tmp;
        }
        return a;
    }
    
    vector<vector<int>> shiftGrid(vector<vector<int>>& grid, int k) {
        int n = grid.size();
        if (n == 0) {
            return grid;
        }
        int m = grid[0].size(), total = n * m;
        k %= total;
        if (k == 0) {
            return grid;
        }
        int i = 0, j = 0, pre = 0, cur = 0, pos = 0, g = gcd(total, k), t = total / g;
        for (i = 0; i < g; ++i) {
            pos = i;
            pre = grid[i / m][i % m];
            for (j=0; j<t; ++j) {
                pos += k;
                pos %= total;
                cur = grid[pos / m][pos % m];
                grid[pos / m][pos % m] = pre;
                pre = cur;
            }
        }
        return grid;
    }
};

5264. 在受污染的二叉树中查找元素

给出一个满足下述规则的二叉树:
root.val == 0
如果 treeNode.val == xtreeNode.left != null,那么 treeNode.left.val == 2 * x + 1
如果 treeNode.val == xtreeNode.right != null,那么 treeNode.right.val == 2 * x + 2
现在这个二叉树受到「污染」,所有的 treeNode.val 都变成了 -1。

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

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

示例 1:
输入:
[“FindElements”,“find”,“find”]
[[[-1,null,-1]],[1],[2]]
输出:
[null,false,true]
解释:
FindElements findElements = new FindElements([-1,null,-1]);
findElements.find(1); // return False
findElements.find(2); // return True

示例 2:
输入:
[“FindElements”,“find”,“find”,“find”]
[[[-1,-1,-1,-1,-1]],[1],[3],[5]]
输出:
[null,true,true,false]
解释:
FindElements findElements = new FindElements([-1,-1,-1,-1,-1]);
findElements.find(1); // return True
findElements.find(3); // return True
findElements.find(5); // return False

示例 3:
输入:
[“FindElements”,“find”,“find”,“find”,“find”]
[[[-1,null,-1,-1,null,-1]],[2],[3],[4],[5]]
输出:
[null,true,false,false,true]
解释:
FindElements findElements = new FindElements([-1,null,-1,-1,null,-1]);
findElements.find(2); // return True
findElements.find(3); // return False
findElements.find(4); // return False
findElements.find(5); // return True

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

思路

简单的dfs. find(n)的时间复杂度为O(logn).

代码

/**
 * 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 {
private:
    TreeNode * _root;
    
    void recover(TreeNode* root, int value) {
        root->val = value;
        if (root->left) {
            recover(root->left, 2 * value + 1);
        }
        if (root->right) {
            recover(root->right, 2 * value + 2);
        }
    }
    
    bool recuFind(TreeNode* root, int target) {
        if (!root) {
            return false;
        }
        if (root->val == target) {
            return true;
        }
        if (target > root->val && target <= 2 * (root->val)) {
            return false;
        }
        return recuFind(root->left, target) || recuFind(root->right, target);
    }
    
public:
    FindElements(TreeNode* root) {
        recover(root, 0);
        _root = root;
    }
    
    bool find(int target) {
        return recuFind(_root, target);
    }
};

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

5265. 可被三整除的最大和

给你一个整数数组 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

思路

贪心。求所有数字的和total,如果total模3余1,则去掉最小的一个模3余1的数或者最小的两个模3余2的数,如果total模3余2,则去掉最小的一个模3余2的数或者最小的两个模3余1的数

代码

class Solution {
public:
    int maxSumDivThree(vector<int>& nums) {
        const int MAX = 0x3f3f3f3f;
        int total = 0, mod1min = MAX, mod1min2 = MAX, mod2min = MAX, mod2min2 = MAX, idx1 = -1, idx2 = -1, i = 0;
        i = 0;
        for (auto num: nums) {
            total += num;
            if (num % 3 == 1) {
                if (num < mod1min) {
                    mod1min = num;
                    idx1 = i;
                }
            } else if (num % 3 == 2) {
                if (num < mod2min) {
                    mod2min = num;
                    idx2 = i;
                }
            }
            ++i;
        }
        i = 0;
        for (auto num: nums) {
            if (num % 3 == 1) {
                if (i != idx1 && num >= mod1min && num < mod1min2) {
                    mod1min2 = num;
                }
            } else if (num % 3 == 2) {
                if (i != idx2 && num >= mod2min && num < mod2min2) {
                    mod2min2 = num;
                }
            }
            ++i;
        }
        if (total % 3 == 0) {
            return total;
        } else if (total % 3 == 1) {
            return max(total - mod1min, total - mod2min - mod2min2);
        } else {
            return max(total - mod1min - mod1min2, total - mod2min);
        }
    }
};

5266. 推箱子

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

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

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

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

示例 1:
输入:grid = [["#","#","#","#","#","#"],
["#",“T”,"#","#","#","#"],
["#",".",".",“B”,".","#"],
["#",".","#","#",".","#"],
["#",".",".",".",“S”,"#"],
["#","#","#","#","#","#"]]
输出:3
解释:我们只需要返回推箱子的次数。

示例 2:
输入:grid = [["#","#","#","#","#","#"],
["#",“T”,"#","#","#","#"],
["#",".",".",“B”,".","#"],
["#","#","#","#",".","#"],
["#",".",".",".",“S”,"#"],
["#","#","#","#","#","#"]]
输出:-1

示例 3:
输入:grid = [["#","#","#","#","#","#"],
["#",“T”,".",".","#","#"],
["#",".","#",“B”,".","#"],
["#",".",".",".",".","#"],
["#",".",".",".",“S”,"#"],
["#","#","#","#","#","#"]]
输出:5
解释:向下、向左、向左、向上再向上。

示例 4:
输入:grid = [["#","#","#","#","#","#","#"],
["#",“S”,"#",".",“B”,“T”,"#"],
["#","#","#","#","#","#","#"]]
输出:-1

提示:

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

思路

如果只考虑箱子的移动,则就是简单的bfs求最短路。由于还要考虑人推箱子的行为,所以在箱子每次移动的时候判断人能不能走到推箱子需要的位置

代码

class Solution {
private:
    struct Node {
        int bx, by, sx, sy, cnt;

        Node(): bx(0), by(0), sx(0), sy(0), cnt(0) {}

        Node(int _bx, int _by, int _sx, int _sy, int _cnt): bx(_bx), by(_by), sx(_sx), sy(_sy), cnt(_cnt) {}
    };

    const int MOV[4][2] = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}};

    inline bool isValid(int bx, int by, int sx, int sy, int n, int m, const vector<vector<char>>& grid) {
        return bx >= 0 && bx < n && by >= 0 && by < m && sx >=0 && sx < n && sy >= 0 && sy < m && grid[bx][by] != '#' && grid[sx][sy] != '#';
    }

    // inner dfs
    bool dfs(int px, int py, int nx, int ny, int bx, int by, int n, int m, bool (*svis)[22], const vector<vector<char>>& grid) {
        if (px == nx && py == ny) {
            return true;
        }
        int rx = 0, ry = 0;
        for (int i = 0; i < 4; ++i) {
            rx = px + MOV[i][0];
            ry = py + MOV[i][1];
            if (rx >= 0 && rx < n && ry >= 0 && ry < m && (rx != bx || ry != by) && grid[rx][ry] != '#' && !svis[rx][ry]) {
                svis[rx][ry] = 1;
                if (dfs(rx, ry, nx, ny, bx, by, n, m, svis, grid)) {
                    return true;
                }
            }
        }
        return false;
    }

public:
    int minPushBox(vector<vector<char>>& grid) {
        bool vis[22][22][22][22] = {0}, svis[22][22] = {0};
        int n = grid.size(), m = grid[0].size(), i = 0, j = 0, bx = 0, by = 0, tx = 0, ty = 0, sx = 0, sy = 0;
        for (i=0; i<n; ++i) {
            for (j=0; j<m; ++j) {
                switch(grid[i][j]) {
                    case 'S':
                        sx = i;
                        sy = j;
                        break;
                    case 'B':
                        bx = i;
                        by = j;
                        break;
                    case 'T':
                        tx = i;
                        ty = j;
                        break;
                }
            }
        }
        // outer bfs
        queue<Node> q;
        Node head;
        int nbx = 0, nby = 0, nsx = 0, nsy = 0;
        vis[bx][by][sx][sy] = 1;
        q.emplace(bx, by, sx, sy, 0);
        while (!q.empty()){
            head = q.front();
            q.pop();
            if (head.bx == tx && head.by == ty) {
                return head.cnt;
            }
            for (i=0; i<4; ++i) {
                nbx = head.bx + MOV[i][0];
                nby = head.by + MOV[i][1];
                nsx = head.bx - MOV[i][0];
                nsy = head.by - MOV[i][1];
                memset(svis, 0, sizeof(svis));
                svis[head.sx][head.sy] = 1;
                if (isValid(nbx, nby, nsx, nsy, n, m, grid) && !vis[nbx][nby][nsx][nsy] && dfs(head.sx, head.sy, nsx, nsy, head.bx, head.by, n, m, svis, grid)) {
                    q.emplace(nbx, nby, nsx, nsy, head.cnt + 1);
                    vis[nbx][nby][nsx][nsy] = 1;
                }
            }
        }
        return -1;
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值