On the shoulders of giants.

weekly-contest-163

二维网格迁移

给你一个 nm 列的二维网格 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
题解

模拟转移即可,可以用长度为 n 的数组当中介容器进行转移。

时间复杂度:\(O(knm)\)

class Solution {
public:
    vector<vector<int>> shiftGrid(vector<vector<int>>& grid, int k) {
        int n = grid.size(), m = grid[0].size();
        vector<int> now(n);
        
        while(k--) {
            for(int i = 0; i < n; i++) {
                now[i] = grid[i][m - 1];
            }
            for(int j = m - 1; 0 < j; j--) {
                for(int i = 0; i < n; i++) {
                    grid[i][j] = grid[i][j - 1];
                }
            }
            for(int i = 0; i < n; i++) {
                grid[i][0] = now[(i - 1 + n) % n];
            }
        }
        
        return grid;
    }
};

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

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

  1. root.val == 0
  2. 如果 treeNode.val == x 且 treeNode.left != null,那么 treeNode.left.val == 2 * x + 1
  3. 如果 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
题解

我们先按树的结构,根据题意,把污染后的树还原,然后还原的值放入集合中,每次判断待查找的数字是否在集合中。

时间复杂度:\(O(n+m \log n)\), n 为树节点个数, m 为查找总次数。

/**
 * 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:
    set<int> s;
    
    void dfs(TreeNode* p) {
        s.insert(p->val);
        if(p->left != NULL) {
            p->left->val = p->val * 2 + 1;
            dfs(p->left);
        }
        if(p->right != NULL) {
            p->right->val = p->val * 2 + 2;
            dfs(p->right);
        }
    }
    
    FindElements(TreeNode* root) {
        root->val = 0;
        dfs(root);
    }
    
    bool find(int target) {
        return s.find(target) != s.end();
    }
};

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

可被三整除的最大和

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

动态规划:设 dp[i][k] 为在 nums[0]nuns_[i] 中余数是 k 的和的最大值。

所以有,\( dp_{i,(k+x) \% 3}=\max (dp_{i-1,k}+x) \) ,可以使用滚动数组优化。

时间复杂度:\( O(n) \)

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

        for(auto x : nums) {
            tmp[0] = tmp[1] = tmp[2] = -1;
            for(int k = 0; k < 3; k++) {
                if(dp[k] == -1) continue;
                tmp[(k + x) % 3] = max(tmp[(k + x) % 3], dp[k] + x);
            }
            for(int k = 0; k < 3; k++) {
                dp[k] = max(dp[k], tmp[k]);
            }
        }

        return dp[0];
    }
};

推箱子

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

游戏地图用大小为 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:定义状态 \( (person_{x,y},box_{x,y}) \) 四个量,然后 spfa 跑最短路。

状态转移有两类:

  • person 移动时没有推 box,那么合法的时候状态进行转移,但是对答案无贡献。
  • person 移动时推动了 box,那么合法的时候状态进行转移,对答案贡献为 1

时间复杂度:\( O(n^2m^2) \)

class Solution {
private:
    const int dt[2][4] = {{0, -1, 0, 1}, {1, 0, -1, 0}}, INF = 0x3f3f3f3f;
public:
    int n, m, spx, spy, sbx, sby, tx, ty, result = INF;

    struct Node {
        int px, py, bx, by;
        bool operator < (const Node& tmp) const {
            if(px != tmp.px || py != tmp.py) {
                return px < tmp.px || (px == tmp.px && py < tmp.py);
            } else {
                return bx < tmp.bx || (bx == tmp.bx && by < tmp.by);
            }
        }
    };

    queue<Node> q;
    map<Node, int> dis;
    map<Node, bool> vis;

    void spfa(vector<vector<char>>& grid, Node s) {
        while(!q.empty()) q.pop();
        vis.clear();
        dis.clear();
        q.push(s);
        dis[s] = 0;
        vis[s] = true;
        while(!q.empty()) {
            Node node = q.front(); q.pop();
            vis[node] = false;
            if(node.bx == tx && node.by == ty) {
                result = min(result, dis[node]);
            }
            for(int i = 0; i < 4; i++) {
                int dx = node.px + dt[0][i], dy = node.py + dt[1][i];
                if(dx < 0 || n <= dx || dy < 0 || m <= dy) continue;
                if(grid[dx][dy] != '.') continue;
                if(dx != node.bx || dy != node.by) {
                    Node e = (Node){dx, dy, node.bx, node.by};
                    if(dis.find(e) == dis.end()) vis[e] = false, dis[e] = INF;
                    if(dis[e] > dis[node]) {
                        dis[e] = dis[node];
                        if(vis[e] == true) continue;
                        vis[e] = true;
                        q.push(e);
                    }
                } else {
                    int bdx = dx + dt[0][i], bdy = dy + dt[1][i];
                    if(bdx < 0 || n <= bdx || bdy < 0 || m <= bdy) continue;
                    if(grid[dx][dy] != '.') continue;
                    Node e = (Node){dx, dy, bdx, bdy};
                    if(dis.find(e) == dis.end()) vis[e] = false, dis[e] = INF;
                    if(dis[e] > dis[node] + 1) {
                        dis[e] = dis[node] + 1;
                        if(vis[e] == true) continue;
                        vis[e] = true;
                        q.push(e);
                    }
                }
            }
        }
    }

    int minPushBox(vector<vector<char>>& grid) {
        n = grid.size(), m = grid[0].size();

        for(int i = 0; i < n; i++) {
            for(int j = 0; j < m; j++) {
                if(grid[i][j] == 'S') spx = i, spy = j, grid[i][j] = '.';
                if(grid[i][j] == 'B') sbx = i, sby = j, grid[i][j] = '.';
                if(grid[i][j] == 'T') tx = i, ty = j, grid[i][j] = '.';
            }
        }

        spfa(grid, (Node){spx, spy, sbx, sby});

        return result == INF ? -1 : result;
    }
};
Share

You may also like...

发表评论

电子邮件地址不会被公开。 必填项已用*标注