本文章的所有题目信息都来源于leetcode
如有侵权请联系我删掉
今天的每日一题是:764. 最大加号标志
在一个 n x n 的矩阵 grid 中,除了在数组 mines 中给出的元素为 0,其他每个元素都为 1。mines[i] = [xi, yi]表示 grid[xi][yi] == 0
返回 grid 中包含 1 的最大的 轴对齐 加号标志的阶数 。如果未找到加号标志,则返回 0 。
一个 k 阶由 1 组成的 “轴对称”加号标志 具有中心网格 grid[r][c] == 1 ,以及4个从中心向上、向下、向左、向右延伸,长度为 k-1,由 1 组成的臂。注意,只有加号标志的所有网格要求为 1 ,别的网格可能为 0 也可能为 1 。
示例 1:
输入: n = 5, mines = [[4, 2]]
输出: 2
解释: 在上面的网格中,最大加号标志的阶只能是2。一个标志已在图中标出。
示例 2:
输入: n = 1, mines = [[0, 0]]
输出: 0
解释: 没有加号标志,返回 0 。
方法一:暴力法,通过题目给出的为0的位置,构建出一个二维矩阵,然后遍历每一个点判断其四个方向是否为1。
方法二:动态规划:由于通过暴力法,我发现遍历每一个点的时候都需要去重新计算其在四个方向上的连续长度,这样子重复了好多遍,浪费时间,于是我选择用4个二维dp数组去存贮相应4个方向的连续长度。
方法三:动态规划:官解中只用了一个二维数组去存贮,但是他原来的想法也是用四个,但是仔细思考了一下,要选取四个方向中最小的那个长度所以其实只用一个就可以了
class Solution {
public:
int orderOfLargestPlusSign(int n, vector<vector<int>>& mines) {
int dx[4] = {-1,0,1,0},dy[4] = {0,1,0,-1};//遍历四个方向
vector<vector<int>> arr(n,vector<int>(n,1));//所有元素初始化为1 抠出来mines中的0就行
for(int i=0;i<mines.size();++i){//将题目给的0的位置进行标记
int x = mines[i][0];
int y = mines[i][1];
arr[x][y] = 0;
}
int ans = 0;
for(int i=0;i<n;++i){
for(int j=0;j<n;++j){
if(arr[i][j] == 1){//判断是否为1
int count = 1;//初始化为1
bool flag = true;//标记,用来退出循环
while(flag){//只要四个方向都为1 就继续延伸
for(int k=0;k<4;++k){
//获取当前位置
int new_x = i+dx[k]*count;
int new_y = j+dy[k]*count;
//如果当前下标越界或者不等于1那么应该退出循环
if(!(new_x >= 0 && new_x < n && new_y >=0 && new_y < n && arr[new_x][new_y] == 1)){
flag = false;
break;
}
}
//只有在满足上下左右四个方向都为1的时候才能让阶数+1
if(flag) {
count++;
}
}
//更新答案
ans = max(ans,count);
}
}
}
return ans;
}
};
class Solution {
public:
int orderOfLargestPlusSign(int n, vector<vector<int>>& mines) {
vector<vector<int>> arr(n,vector<int>(n,1));//所有位置初始化为1
for(int i=0;i<mines.size();++i){//将题目给的0点标记出来
int x = mines[i][0];
int y = mines[i][1];
arr[x][y] = 0;
}
vector<vector<int>> l(n,vector<int>(n,0));//左
//l[i][j]表示的是第i行中j及j左边连续1的个数
for(int i=0;i<n;++i){//初始化的是矩阵的最左边的边
if(arr[i][0] == 1) l[i][0] = 1;
}
//r[i][j]表示的是第i行中j及j右边连续1的个数
vector<vector<int>> r(n,vector<int>(n,0));//右
for(int i=0;i<n;++i){//初始化的是矩阵的最右边的边
if(arr[i][n-1] == 1) r[i][n-1] = 1;
}
//u[i][j]表示的是第i行中j及j上边连续1的个数
vector<vector<int>> u(n,vector<int>(n,0));//上
for(int i=0;i<n;++i){//初始化的是矩阵的最上边的边
if(arr[0][i] == 1) u[0][i] = 1;
}
//d[i][j]表示的是第i行中j及j下边连续1的个数
vector<vector<int>> d(n,vector<int>(n,0));//下
for(int i=0;i<n;++i){//初始化的是矩阵的最下边的边
if(arr[n-1][i] == 1) d[n-1][i] = 1;
}
for(int i=0;i<n;++i){
for(int j=1;j<n;++j){//注意这里从1开始,因为会有j-1的判断,而我们已经对所有j=0的位置都进行了初始化
if(arr[i][j] == 1){//对于左边数组来说 如果当前值为1那么当前最长连续1的个数就是前一个位置最长连续1的个数+1 右,上,下同理
l[i][j] = l[i][j-1] + 1;
}
if(arr[i][n-1-j] == 1){
r[i][n-1-j] = r[i][n-j] + 1;
}
if(arr[j][i] == 1){
u[j][i] = u[j-1][i] + 1;
}
if(arr[n-1-j][i] == 1){
d[n-1-j][i] = d[n-j][i] + 1;
}
}
}
//遍历上下左右数组,找到答案
int res = 0;
for(int i=0;i<n;++i){
for(int j=0;j<n;++j){
//对于一个加号标志,我们找的是最短的一条边才是他的阶数
int temp = min(l[i][j], min(r[i][j], min(u[i][j], d[i][j])));
//更新答案,这里是和其他位置的加号标志的阶数进行比较,所以去较大值
res = max(res,temp);
}
}
return res;
}
};
class Solution {
public:
int orderOfLargestPlusSign(int n, vector<vector<int>>& mines) {
vector<vector<int>> dp(n, vector<int>(n, n));
unordered_set<int> banned;
for (auto &&vec : mines) {
banned.emplace(vec[0] * n + vec[1]);
}
int ans = 0;
for (int i = 0; i < n; i++) {
int count = 0;
/* left */
for (int j = 0; j < n; j++) {
if (banned.count(i * n + j)) {
count = 0;
} else {
count++;
}
dp[i][j] = min(dp[i][j], count);
}
count = 0;
/* right */
for (int j = n - 1; j >= 0; j--) {
if (banned.count(i * n + j)) {
count = 0;
} else {
count++;
}
dp[i][j] = min(dp[i][j], count);
}
}
for (int i = 0; i < n; i++) {
int count = 0;
/* up */
for (int j = 0; j < n; j++) {
if (banned.count(j * n + i)) {
count = 0;
} else {
count++;
}
dp[j][i] = min(dp[j][i], count);
}
count = 0;
/* down */
for (int j = n - 1; j >= 0; j--) {
if (banned.count(j * n + i)) {
count = 0;
} else {
count++;
}
dp[j][i] = min(dp[j][i], count);
ans = max(ans, dp[j][i]);
}
}
return ans;
}
};
读题:暴力!->DP!