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 )则会出现以下错误:
因为矩阵的值为-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;
}
};
模拟法:
用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字形扫描
思路:偏移量法的典型题目
值得注意的细节是:
向右和向下每次只走一步,而向左下,右上可以一直走直到碰到边界。
因此转向的条件变成了:
if(dr == 0 || dr == 2 || x1 < 0 || x1 >= n || y1 < 0 || y1 >= n)
要把
matrix[0][0]单独记录。因为最开始的时候d=0,判断条件里该转向了,那么(0,1)位置的元素就不会读入而直接d= 1往左下去了。因此要从(0,1)位置开始 即:
for( int k = 1 , x = 0 , y = 1 , d = 0)循环轮次,要读取原矩阵的最后一个元素(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;
}




浙公网安备 33010602011771号