这周的周赛,时间冲突,所以是后面补的题目。
第一题,直接按题目的意思进行模拟,时间复杂度过得去
第二题,二叉树,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/
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),不会超时
class Solution {
public:
vector> shiftGrid(vector>& 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;
}
};
https://leetcode-cn.com/problems/find-elements-in-a-contaminated-binary-tree/
给出一个满足下述规则的二叉树:
- root.val == 0
- 如果 treeNode.val == x 且 treeNode.left != null,那么 treeNode.left.val == 2 * x + 1
- 如果 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就查找以下对应值有没有在这个集合中。
/**
* 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 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);
*/
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 的最大和是不存在的,也就是设为最小值,为了后面更新最大值来处理的。
上面用到的是,二维数组来记录,为了节省内存,我们可以用两个一维数组,一个一维数组每次更新,最后把值保留给另一个数组,这样子就实现了和上面用二维数组相同功能,但节省了内存。
#define INF 0x3f3f3f3f
class Solution {
public:
int maxSumDivThree(vector& 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];
}
};
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操作。
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 que;
map inQue;
map dist;
class Solution {
public:
int minPushBox(vector>& 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;
}
};