• 764. 最大加号标志


    764. 最大加号标志

    难度中等126

    在一个 n x n 的矩阵 grid 中,除了在数组 mines 中给出的元素为 0,其他每个元素都为 1mines[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 。
    

    提示:

    • 1 <= n <= 500
    • 1 <= mines.length <= 5000
    • 0 <= xi, yi < n
    • 每一对 (xi, yi) 都 不重复

    分析:本题无非就是要求以某个点(x,y)为中心,向上下左右延申得到的四个长度中的最小值。

    思路1:枚举

            我们可以直接枚举n*n个点,然后再进行四个方向上的长度探索,复杂度为O(n*n*4n)=O(n^3)。

    思路2:枚举优化-值维护

            如果我们可以遍历一遍所有的点就知道每一个点上下左右的长度,就可以直接得到每个点位置的加号标志长度了。这可能吗?我们来试一下。假设我们现在有一个数组left_len[i][j],代表着以(i,j)为中心,向左延伸的最大长度,那么我们容易推知

    left\_len[i][j]=\left\{ \begin{matrix} left\_len[i][j-1] + 1, grid[i][j]==1 \\ 0, grid[i][j]==0 \end{matrix}\right.

    那么类似的可以得到

    up\_len[i][j]=\left\{ \begin{matrix} up\_len[i - 1][j] + 1, grid[i][j]==1 \\ 0, grid[i][j]==0 \end{matrix}\right.

    right\_len[i][j]=\left\{ \begin{matrix} right\_len[i][j +1] + 1, grid[i][j]==1 \\ 0, grid[i][j]==0 \end{matrix}\right.

    buttom\_len[i][j]=\left\{ \begin{matrix} buttom\_len[i + 1][j] + 1, grid[i][j]==1 \\ 0, grid[i][j]==0 \end{matrix}\right.

    只需要两次遍历,从左上向右下遍历可以得到left_lenup_len,从右下往左上遍历可以得到right_lenbuttom_len,那么可以推知:

    len[i,j]=min(left\_len[i][j], right\_len[i][j], up\_len[i][j], buttom\_len[i][j])

    1. class Solution {
    2. public:
    3. int orderOfLargestPlusSign(int n, vectorint>>& mines) {
    4. vectorint>> grid = vector(n, vector<int>(n, 1));
    5. vectorint>> left_len = vector(n, vector<int>(n, 0));
    6. vectorint>> right_len = vector(n, vector<int>(n, 0));
    7. vectorint>> up_len= vector(n, vector<int>(n, 0));
    8. vectorint>> bottom_len = vector(n, vector<int>(n, 0));
    9. for(vector mine:mines){
    10. grid[mine[0]][mine[1]] = 0;
    11. }
    12. int i, j, max_len = n * n == mines.size() ? 0 : 1;
    13. for(i = 0; i < n; ++ i){
    14. up_len[0][i] = grid[0][i];
    15. left_len[i][0] = grid[i][0];
    16. if(i > 0){
    17. up_len[i][0] = grid[i][0] == 1 ? 1 + up_len[i - 1][0] : 0;
    18. left_len[0][i] = grid[0][i] == 1 ? 1 + left_len[0][i - 1] : 0;
    19. }
    20. }
    21. for(i = 1; i < n; ++ i){
    22. for(j = 1; j < n; ++ j){
    23. up_len[i][j] = grid[i][j] == 0 ? 0 : up_len[i - 1][j] + 1;
    24. left_len[i][j] = grid[i][j] == 0 ? 0 : left_len[i][j - 1] + 1;
    25. }
    26. }
    27. for(i = n - 1; i >= 0; -- i){
    28. bottom_len[n - 1][i] = grid[n - 1][i];
    29. right_len[i][n - 1] = grid[i][n - 1];
    30. if(i < n - 1){
    31. bottom_len[i][n - 1] = grid[i][n - 1] == 1 ? 1 + bottom_len[i + 1][n - 1] : 0;
    32. right_len[n - 1][i] = grid[n - 1][i] == 1 ? 1 + right_len[n - 1][i + 1] : 0;
    33. }
    34. }
    35. for(i = n - 2; i >= 1; -- i){
    36. for(j = n - 2; j >= 1; -- j){
    37. bottom_len[i][j] = grid[i][j] == 0 ? 0 : bottom_len[i + 1][j] + 1;
    38. right_len[i][j] = grid[i][j] == 0 ? 0 : right_len[i][j + 1] + 1;
    39. max_len = max(max_len, min(min(up_len[i][j], left_len[i][j]), min(bottom_len[i][j], right_len[i][j])));
    40. }
    41. }
    42. return max_len;
    43. }
    44. };

    思路3:空间节约

            我们需要像思路2一样另外存储四个数组吗?倒不见得。我们知道上面之所以不用一个数组来存储是因为害怕对后续的更新有影响。举个例子:

            如上图所示,grid[2][1]=1,且这个位置的最大长度为1即len[2][1]=1。而现在我们考虑(2,2),理想情况下,我们应该从他的四个邻接位置中取一个最小值+1,就得到答案了,然而实际上我们不能这样做,以上面的为例,我们取左邻接(2,1)的长度为1,其他三个位置都是2,然而实际的答案是3并不是1+1=2。原因在于我们本想借助(2,1)来获取(2,2)的最大左边长度,然后实际上len[2][1]给我们的结果却是(2,1)考虑了(1,1)位置的0的答案。

            所以推知,我们没有办法在上一步已经考虑了四条边(其实我们最多一次性智能考虑两条边、左上-右下,左下-右上都可)的情况下得到正确答案。而正确的做法是将上下左右的遍历单独运算。

    1. class Solution {
    2. public:
    3. void printf_mat(vectorint>>& dp, int n){
    4. for(int i = 0; i < n; ++ i){
    5. for(int j = 0; j < n; ++ j){
    6. printf("%d ", dp[i][j]);
    7. }
    8. printf("\n");
    9. }
    10. }
    11. int orderOfLargestPlusSign(int n, vectorint>>& mines) {
    12. vectorint>> dp = vector(n, vector<int>(n, n));
    13. unordered_set<int> banned_idx;
    14. for(vector<int> mine:mines){
    15. banned_idx.emplace(mine[0] * n + mine[1]);
    16. }
    17. // from left to right
    18. //printf("l -> r:\n");
    19. int continue_one_num, i, j, max_len = 0;
    20. for(i = 0; i < n; ++ i){
    21. continue_one_num = 0;
    22. for(j = 0; j < n; ++ j){
    23. if(banned_idx.find(i * n + j) == banned_idx.end()){
    24. ++ continue_one_num;
    25. }else{
    26. continue_one_num = 0;
    27. }
    28. dp[i][j] = min(dp[i][j], continue_one_num);
    29. }
    30. }
    31. //printf_mat(dp, n);
    32. // from up to buttom
    33. //printf("u -> b:\n");
    34. continue_one_num = 0;
    35. for(i = 0; i < n; ++ i){
    36. continue_one_num = 0;
    37. for(j = 0; j < n; ++ j){
    38. if(banned_idx.find(j * n + i) == banned_idx.end()){
    39. ++ continue_one_num;
    40. }else{
    41. continue_one_num = 0;
    42. }
    43. dp[j][i] = min(dp[j][i], continue_one_num);
    44. }
    45. }
    46. //printf_mat(dp, n);
    47. // from right to left
    48. //printf("r -> l:\n");
    49. continue_one_num = 0;
    50. for(i = 0; i < n; ++ i){
    51. continue_one_num = 0;
    52. for(j = n - 1; j >= 0; --j){
    53. if(banned_idx.find(i * n + j) == banned_idx.end()){
    54. ++ continue_one_num;
    55. }else{
    56. continue_one_num = 0;
    57. }
    58. dp[i][j] = min(dp[i][j], continue_one_num);
    59. }
    60. }
    61. //printf_mat(dp, n);
    62. // from buttom to up
    63. //printf("b -> u:\n");
    64. continue_one_num = 0;
    65. for(i = 0; i < n; ++ i){
    66. continue_one_num = 0;
    67. for(j = n - 1; j >= 0; -- j){
    68. if(banned_idx.find(j * n + i) == banned_idx.end()){
    69. ++ continue_one_num;
    70. }else{
    71. continue_one_num = 0;
    72. }
    73. dp[j][i] = min(dp[j][i], continue_one_num);
    74. max_len = max(max_len, dp[j][i]);
    75. }
    76. }
    77. //printf_mat(dp, n);
    78. return max_len;
    79. }
    80. };

  • 相关阅读:
    Linux命令(93)之su
    目前很火的养猫微信小程序源码带流量主+搭建教程
    Linux系统中的用户与组是什么?
    python工具-c-struct-decode
    算法-二分查找
    EasyRecovery免费版一键数据恢复还原软件
    JVM - 双亲委派
    python链接数据库并创建/删除/插入多个数据库/表/表数据
    刷题记录(NC208250 牛牛的最美味和最不美味的零食)
    Linux 内存管理知识总结(二)
  • 原文地址:https://blog.csdn.net/qq_39304630/article/details/127768533