11.8偏移量法

11/8

每日一题:H:3235.判断矩形的两个角落是否可达

太难了,跳过。

class Solution {
    // 判断点 (x,y) 是否在圆 (ox,oy,r) 内
    bool in_circle(long long ox, long long oy, long long r, long long x, long long y) {
        return (ox - x) * (ox - x) + (oy - y) * (oy - y) <= r * r;
    }

public:
    bool canReachCorner(int X, int Y, vector<vector<int>>& circles) {
        int n = circles.size();
        vector<int> vis(n);
        auto dfs = [&](auto&& dfs, int i) -> bool {
            long long x1 = circles[i][0], y1 = circles[i][1], r1 = circles[i][2];
            // 圆 i 是否与矩形右边界/下边界相交相切
            if (y1 <= Y && abs(x1 - X) <= r1 ||
                x1 <= X && y1 <= r1 ||
                x1 > X && in_circle(x1, y1, r1, X, 0)) {
                return true;
            }
            vis[i] = true;
            for (int j = 0; j < n; j++) {
                long long x2 = circles[j][0], y2 = circles[j][1], r2 = circles[j][2];
                // 在两圆相交相切的前提下,点 A 是否严格在矩形内
                if (!vis[j] && (x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2) <= (r1 + r2) * (r1 + r2) &&
                    x1 * r2 + x2 * r1 < (r1 + r2) * X &&
                    y1 * r2 + y2 * r1 < (r1 + r2) * Y &&
                    dfs(dfs, j)) {
                    return true;
                }
            }
            return false;
        };
        for (int i = 0; i < n; i++) {
            long long x = circles[i][0], y = circles[i][1], r = circles[i][2];
            if (in_circle(x, y, r, 0, 0) || // 圆 i 包含矩形左下角
                in_circle(x, y, r, X, Y) || // 圆 i 包含矩形右上角
                // 圆 i 是否与矩形上边界/左边界相交相切
                !vis[i] && (x <= X && abs(y - Y) <= r ||
                            y <= Y && x <= r ||
                            y > Y && in_circle(x, y, r, 0, Y)) && dfs(dfs, i)) {
                return false;
            }
        }
        return true;
    }
};
/*
作者:灵茶山艾府
链接:https://leetcode.cn/problems/check-if-the-rectangle-corner-is-reachable/solutions/2860214/deng-jie-zhuan-huan-bing-cha-ji-pythonja-yf9y
*/

59.螺旋矩阵II

NOT AC

思路:能想到是偏移量法模拟,具体实现过程不熟练

具体地,因为题目函数是要返回一个二维矩阵,因此先定义一个二维矩阵res。

定义偏移量dx , dy,这里的x、y分别代表行、列。即往下是X轴正向,往右是Y轴正向。

d代表方向,初始是向右 , 列+1的方向,因此初始偏移dx[d] = 0 , dy[d] = 1。

x1 ,y1代表走一步后的方向,用来试探,如果能走就赋给X , Y。不能走说明要顺时针转向了d + 1,再走。

class Solution {
public:
    vector<vector<int>> generateMatrix(int n) {
        vector<vector<int>> res(n , vector<int>(n , 0));
      	//右->下->左->上
        int dx[4] = {0 , 1 ,  0 , -1};
        int dy[4] = {1 , 0 , -1 , 0 };
        for (int k = 1 , x = 0 , y = 0 , d = 0 ; k <= n * n ; k ++){
            res[x][y] = k;
            int x1 = x + dx[d];
            int y1 = y + dy[d];
            if(x1 < 0 || x1 >= n || y1 < 0 || y1 >= n || res[x1][y1]){
              d = (d + 1) % 4;
              x1 = x + dx[d];
              y1 = y + dy[d];
            }
            x = x1 , y = y1;
        }
        return res;
    }
};

ACwing756.蛇形矩阵

几乎是上一题的ACM模式,思路一样。

#include <iostream>
#include <vector>
using namespace std;

int main(){
  int n , m;
  cin >> n >> m;
  vector<vector<int>> res(n , vector<int>(m , 0));
  int dx[4] = {0 , 1 , 0 , -1};
  int dy[4] = { 1 , 0 ,  -1 , 0};
  for (int k = 1 , x = 0 ,y = 0 , d = 0 ; k <= n * m ; k ++){
    res[x][y] = k;
    int x1 = x + dx[d];
    int y1 = y + dy[d];
    if(x1 < 0 || x1 >= n || y1 < 0 || y1 >= m || res[x1][y1]){
      d = (d + 1) % 4;
      x1 = x + dx[d];
      y1 = y + dy[d];
    }
    x = x1 , y = y1;
  }
  for (int i = 0; i < n; i++) {
     for (int j = 0; j < m; j++) {
        cout << res[i][j] << " ";
     }
     cout << endl;
  }
  return 0;
}

54.螺旋矩阵

与上面的螺旋矩阵2属于互补题,挺好的。

思路:

揭露了一个隐藏的易错点即:如果对下一步的判断条件中不把已经走过的刨除,如:

 if(x1 < 0 || x1 >= m  || y1 < 0 || y1 >= n )

则会出现以下错误:

image-20241108164452807

因为矩阵的值为-100-100 , 可以把已经遍历过的值更改为105,相当于打上标记。

 matrix[x][y] = 105;
if(maxtrix[x1][y1] == 105)

题目中说了矩阵非空,因此不用特殊判断。

class Solution {
public:
    vector<int> spiralOrder(vector<vector<int>>& matrix) {
      int m = matrix.size();
      int n = matrix[0].size();
        vector<int> res(m * n , 0);
        
      	//右->下->左->上
        int dx[4] = {0 , 1 ,  0 , -1};
        int dy[4] = {1 , 0 , -1 , 0 };
        for (int k = 0 , x = 0 , y = 0 , d = 0 ; k < m * n ; k ++){
           res[k] = matrix[x][y];
           matrix[x][y] = 105;
            int x1 = x + dx[d];
            int y1 = y + dy[d];
            if(x1 < 0 || x1 >= m  || y1 < 0 || y1 >= n || matrix[x1][y1] == 105){
              d = (d + 1) % 4;
              x1 = x + dx[d];
              y1 = y + dy[d];
            }
            x = x1 , y = y1;
        }
        return res;
    }
};

模拟法:

image-20241108165642212

用t , r , b , l 分别代表上右下左四个边界。

class Solution {
public:
    vector<int> spiralOrder(vector<vector<int>>& matrix) {
      int l = 0;
      int r = matrix[0].size() - 1; //列数
      int t = 0;  //top上边界
      int b = matrix.size() - 1; //bottom下边界
      vector<int> res;
        while(1){
          //左->右
          for (int i = l ; i <= r ; i ++) res.push_back(matrix[t][i]);
          //紧接着往下,收缩上边界,判断此时上下是否重合
          if(++ t > b)  break;
          //上->下
          for (int i = t ; i <= b ; i ++)  res.push_back(matrix[i][r]);
          //紧接着往左,收缩右边界,判断此时左右是否重合
          if(l > --r) break;
          //右->左
          for (int i = r ; i >= l ; i --)  res.push_back(matrix[b][i]);
          //紧接着往上,收缩下边界,判断此时上下是否重合
          if(t > --b ) break;
          //下->上
          for (int i = b ; i >= t ; i --) res.push_back(matrix[i][l]);
          //紧接着往右,收缩左边界,判断此时左右是否重合
          if(++l > r ) break;
        }      
        return res;
    }
};

剑指Offer 29.顺时针打印矩阵(opens new window)

这题array.size()可能为0,因此需要加特判,其余一样。

class Solution {
public:
     vector<int> spiralArray(vector<vector<int>>& array) {
      if(array.size() == 0)  return {};
      int l = 0;
      int r = array[0].size() - 1; //列数
      int t = 0;  //top上边界
      int b = array.size() - 1; //bottom下边界
      vector<int> res;
        while(1){
          //左->右
          for (int i = l ; i <= r ; i ++) res.push_back(array[t][i]);
          //紧接着往下,收缩上边界,判断此时上下是否重合
          if(++ t > b)  break;
          //上->下
          for (int i = t ; i <= b ; i ++)  res.push_back(array[i][r]);
          //紧接着往左,收缩右边界,判断此时左右是否重合
          if(l > --r) break;
          //右->左
          for (int i = r ; i >= l ; i --)  res.push_back(array[b][i]);
          //紧接着往上,收缩下边界,判断此时上下是否重合
          if(t > --b ) break;
          //下->上
          for (int i = b ; i >= t ; i --) res.push_back(array[i][l]);
          //紧接着往右,收缩左边界,判断此时左右是否重合
          if(++l > r ) break;
        }      
        return res;
    }
};

ACwing3208.Z字形扫描

思路:偏移量法的典型题目

image-20241108175953001

值得注意的细节是:

  1. 向右和向下每次只走一步,而向左下,右上可以一直走直到碰到边界

    因此转向的条件变成了:

if(dr == 0 || dr == 2 || x1 < 0 || x1 >= n || y1 < 0 || y1 >= n)
  1. 要把matrix[0][0]单独记录。因为最开始的时候d=0,判断条件里该转向了,那么(0,1)位置的元素就不会读入而直接d= 1往左下去了。

    因此要从(0,1)位置开始 即:

    for( int k = 1 , x = 0 , y = 1 , d = 0)
    
  2. 循环轮次,要读取原矩阵的最后一个元素(n,n)至少要跑半个扩充后的图,即2n * 2n / 2 = 2 * n * n,因此k<2*n*n

#include <iostream>
#include <vector>
using namespace std;
const int N = 505;

int main(){
  int n ;
  cin >> n;
  int matrix[2*N][2*N];
  vector<int> res;
  for (int i = 0; i < n; i++) {
        for (int j = 0; j < n; j++) {
          //yxc:当输入规模大于十万时用scanf提高效率。
          scanf("%d" , &matrix[i][j]);
            //cin >> matrix[i][j] ;
        }
  }

  //四个方向, 向右(0,1) 向左下(1,-1)向下(1,0)向右上(-1,1)
  int dx[4] = {0 , 1 , 1 , -1};
  int dy[4] = {1 , -1 , 0 , 1};
  res.push_back(matrix[0][0]);
  for (int k = 1 , x = 0 , y = 1 , d = 0 ; k < 2 * n * n  ; k ++){
    //在原矩阵范围内的收进res
    if(x < n && y < n)   res.push_back(matrix[x][y]); 
    int x1 = x + dx[d];
    int y1 = y + dy[d];
    if(d == 0 || d == 2 || x1 < 0 || x1 >= n || y1 < 0 || y1 >= n){
      d = (d + 1) % 4;
      x1 = x + dx[d];
      y1 = y + dy[d];
    }
    x = x1;
    y = y1;
  }
  for (int i = 0; i < n*n; i++) {
     cout << res[i] << " ";
  }
  return 0;
}
posted @ 2024-11-08 20:20  七龙猪  阅读(1)  评论(0)    收藏  举报
-->