#P2296. 第3题-逆转矩阵列表
          
                        
                                    
                      
        
              - 
          
          
                      1000ms
            
          
                      Tried: 1024
            Accepted: 342
            Difficulty: 4
            
          
          
          
                       所属公司 : 
                              华为
                                
            
                        
              时间 :2024年9月19日-留学生
                              
                      
          
 
- 
                        算法标签>模拟          
 
第3题-逆转矩阵列表
原题来源
题面解释:
在本题中,我们需要处理一个 ( N \times N ) 的矩阵,每个元素为正整数,且所有正整数从 ( 1 ) 到 ( N^2 ) 恰好出现一次。给定该矩阵的顺时针遍历的结果,要求输出对应的逆时针遍历结果。输入格式为第一行包含一个整数 ( N ),表示矩阵的大小;第二行输入 ( N^2 ) 个整数,表示顺时针链表中每个元素的值。输出格式为 ( N^2 ) 个整数,表示逆时针链表的元素,以空格分隔.
模拟
对于一个大小为n的矩阵,利用一个vis数组先把周围标记墙,从(1,1)开始顺时针(右下左上(循环))模拟走,走过的位置也标记为墙体,碰到墙则进行下一个方向例如(前面一格是墙则反向顺时针旋转90度),一定能走完n*n的矩阵。还原矩阵,同理按逆时针模拟输出即可 整体时间复杂度o(n * n)
题解
在本题中,我们需要处理一个 ( N \times N ) 的矩阵,其中的每个元素都是正整数,且所有的正整数从 ( 1 ) 到 ( N^2 ) 恰好出现一次。我们首先根据给定的顺时针链表生成一个矩阵,然后输出该矩阵的逆时针遍历结果。
思路
- 
初始化访问标记:首先我们定义一个
visited数组,用来标记矩阵的访问状态。矩阵的边界视为墙体(已访问),内部位置初始为未访问。 - 
顺时针填充矩阵:
- 从矩阵的左上角 (1,1) 开始,按顺时针方向(右、下、左、上)进行填充。
 - 如果当前方向可以继续前进(即下一个位置未被访问),则继续向该方向前进。
 - 如果碰到墙(已访问),则顺时针旋转90度,尝试下一个方向。
 
 - 
逆时针输出矩阵:
- 从填充后的矩阵的左上角开始,按逆时针方向(下、右、上、左)进行遍历输出。
 - 同样地,判断下一个位置是否已访问,并适时改变方向。
 
 - 
时间复杂度:整个过程的时间复杂度为 ( O(N^2) ),因为每个位置都会被访问一次。
 
代码如下
cpp
#include <bits/stdc++.h>
using namespace std;
#define MAX_N 105
int n; // 矩阵的阶数
int seq[MAX_N * MAX_N]; // 顺时针链表中的元素值
int mat[MAX_N][MAX_N]; // 矩阵
int visited[MAX_N][MAX_N]; // 访问标记矩阵
int dxClock[4] = {0, 1, 0, -1}; // 顺时针行方向(右、下、左、上)
int dyClock[4] = {1, 0, -1, 0}; // 顺时针列方向
int dxCounter[4] = {1, 0, -1, 0}; // 逆时针行方向(下、右、上、左)
int dyCounter[4] = {0, 1, 0, -1}; // 逆时针列方向
// 初始化访问标记,将矩阵外边界设置为已访问
void initVisitedOuter() {
    for (int i = 0; i <= n + 1; i++) {
        for (int j = 0; j <= n + 1; j++) {
            visited[i][j] = 1; // 设置矩阵边界为已访问
        }
    }
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= n; j++) {
            visited[i][j] = 0; // 矩阵内部未访问
        }
    }
}
// 顺时针递归填充矩阵
void fillClockwise(int x, int y, int dir, int idx) {
    mat[x][y] = seq[idx]; // 填入顺时针链表中的值
    visited[x][y] = 1; // 标记当前位置为已访问
    int nx = x + dxClock[dir]; // 计算下一个位置的行坐标
    int ny = y + dyClock[dir]; // 计算下一个位置的列坐标
    if (visited[nx][ny] == 0) { // 如果下一个位置未访问,继续递归
        fillClockwise(nx, ny, dir, idx + 1);
    } else { // 否则换方向
        dir = (dir + 1) % 4; // 顺时针旋转方向
        nx = x + dxClock[dir]; // 更新下一个位置
        ny = y + dyClock[dir];
        if (visited[nx][ny] == 0) {
            fillClockwise(nx, ny, dir, idx + 1);
        }
    }
}
// 逆时针读取矩阵并输出
void outputCounterClockwise(int x, int y, int dir, int idx) {
    if (idx != 1) cout << ' '; // 在输出前加空格,避免开头多余空格
    cout << mat[x][y]; // 输出当前矩阵位置的值
    visited[x][y] = 1; // 标记当前位置为已访问
    int nx = x + dxCounter[dir]; // 计算下一个位置的行坐标
    int ny = y + dyCounter[dir]; // 计算下一个位置的列坐标
    if (visited[nx][ny] == 0) { // 如果下一个位置未访问,继续递归
        outputCounterClockwise(nx, ny, dir, idx + 1);
    } else { // 否则换方向
        dir = (dir + 1) % 4; // 逆时针旋转方向
        nx = x + dxCounter[dir]; // 更新下一个位置
        ny = y + dyCounter[dir];
        if (visited[nx][ny] == 0) {
            outputCounterClockwise(nx, ny, dir, idx + 1);
        }
    }
}
int main() {
    ios::sync_with_stdio(0); // 加速输入输出
    cin.tie(0); // 解除 cin 和 cout 的绑定
    cin >> n; // 读取矩阵的大小
    for (int i = 1; i <= n * n; i++) cin >> seq[i]; // 读取顺时针链表
    initVisitedOuter(); // 初始化访问矩阵边界
    fillClockwise(1, 1, 0, 1); // 从 (1, 1) 开始顺时针填充矩阵
    initVisitedOuter(); // 重新初始化访问边界,用于逆时针读取
    outputCounterClockwise(1, 1, 0, 1); // 从 (1, 1) 开始逆时针读取并输出
    return 0; // 程序结束
}
python
import sys
sys.setrecursionlimit(100005)  # 增加递归深度以适应较大的矩阵
MAX_N = 105
n = 0
clockwise_values = [0] * (MAX_N * MAX_N)  # 顺时针链表中的元素值
matrix = [[0] * MAX_N for _ in range(MAX_N)]  # 矩阵
visited = [[0] * MAX_N for _ in range(MAX_N)]  # 访问标记矩阵
dx_clock = [0, 1, 0, -1]  # 顺时针方向行移动
dy_clock = [1, 0, -1, 0]  # 顺时针方向列移动
dx_counter = [1, 0, -1, 0]  # 逆时针方向行移动
dy_counter = [0, 1, 0, -1]  # 逆时针方向列移动
# 初始化访问标记,将矩阵内部设置为未访问
def reset_inner_visited():
    global visited
    for i in range(1, n + 1):
        for j in range(1, n + 1):
            visited[i][j] = 0
# 初始化访问标记,将矩阵外边界设置为已访问
def set_outer_boundary_visited():
    global visited
    for i in range(n + 2):
        for j in range(n + 2):
            visited[i][j] = 1
# 顺时针递归填充矩阵
def fill_clockwise(x, y, direction, idx):
    matrix[x][y] = clockwise_values[idx]
    visited[x][y] = 1
    next_x, next_y = x + dx_clock[direction], y + dy_clock[direction]
    if visited[next_x][next_y] == 0:
        fill_clockwise(next_x, next_y, direction, idx + 1)
    else:
        # 换方向
        direction = (direction + 1) % 4
        next_x, next_y = x + dx_clock[direction], y + dy_clock[direction]
        if visited[next_x][next_y] == 0:
            fill_clockwise(next_x, next_y, direction, idx + 1)
# 逆时针读取矩阵并输出
def output_counter_clockwise(x, y, direction, idx):
    if idx != 1:
        print(' ', end='')
    print(matrix[x][y], end='')
    visited[x][y] = 1
    next_x, next_y = x + dx_counter[direction], y + dy_counter[direction]
    if visited[next_x][next_y] == 0:
        output_counter_clockwise(next_x, next_y, direction, idx + 1)
    else:
        # 换方向
        direction = (direction + 1) % 4
        next_x, next_y = x + dx_counter[direction], y + dy_counter[direction]
        if visited[next_x][next_y] == 0:
            output_counter_clockwise(next_x, next_y, direction, idx + 1)
# 主程序入口
n = int(input())
clockwise_values = [0] + list(map(int, input().split()))  # 顺时针链表
set_outer_boundary_visited()  # 设置矩阵外边界已访问
reset_inner_visited()  # 重置内部为未访问
fill_clockwise(1, 1, 0, 1)  # 顺时针填充矩阵
set_outer_boundary_visited()  # 重新设置矩阵外边界
reset_inner_visited()  # 重置内部为未访问
output_counter_clockwise(1, 1, 0, 1)  # 逆时针输出矩阵
java
import java.util.Scanner;
public class Main {
    static final int MAX_N = 105;
    static int n; // 矩阵的阶数
    static int[] clockwiseValues = new int[MAX_N * MAX_N]; // 顺时针链表中的值
    static int[][] matrix = new int[MAX_N][MAX_N]; // 矩阵
    static int[][] visited = new int[MAX_N][MAX_N]; // 访问标记矩阵
    static int[] dxClock = {0, 1, 0, -1}; // 顺时针方向行移动
    static int[] dyClock = {1, 0, -1, 0}; // 顺时针方向列移动
    static int[] dxCounter = {1, 0, -1, 0}; // 逆时针方向行移动
    static int[] dyCounter = {0, 1, 0, -1}; // 逆时针方向列移动
    // 重置矩阵的内部访问标记为未访问
    static void resetInnerVisited() {
        for (int i = 1; i <= n; i++) {
            for (int j = 1; j <= n; j++) {
                visited[i][j] = 0;
            }
        }
    }
    // 初始化访问标记,将矩阵外边界设置为已访问
    static void setOuterBoundaryVisited() {
        for (int i = 0; i <= n + 1; i++) {
            for (int j = 0; j <= n + 1; j++) {
                visited[i][j] = 1;
            }
        }
    }
    // 顺时针递归填充矩阵
    static void fillClockwise(int x, int y, int direction, int idx) {
        matrix[x][y] = clockwiseValues[idx];
        visited[x][y] = 1;
        int nextX = x + dxClock[direction];
        int nextY = y + dyClock[direction];
        
        if (visited[nextX][nextY] == 0) {
            fillClockwise(nextX, nextY, direction, idx + 1);
        } else {
            // 改变方向
            direction = (direction + 1) % 4;
            nextX = x + dxClock[direction];
            nextY = y + dyClock[direction];
            if (visited[nextX][nextY] == 0) {
                fillClockwise(nextX, nextY, direction, idx + 1);
            }
        }
    }
    // 逆时针递归输出矩阵
    static void outputCounterClockwise(int x, int y, int direction, int idx) {
        if (idx != 1) System.out.print(' ');
        System.out.print(matrix[x][y]);
        visited[x][y] = 1;
        int nextX = x + dxCounter[direction];
        int nextY = y + dyCounter[direction];
        
        if (visited[nextX][nextY] == 0) {
            outputCounterClockwise(nextX, nextY, direction, idx + 1);
        } else {
            // 改变方向
            direction = (direction + 1) % 4;
            nextX = x + dxCounter[direction];
            nextY = y + dyCounter[direction];
            if (visited[nextX][nextY] == 0) {
                outputCounterClockwise(nextX, nextY, direction, idx + 1);
            }
        }
    }
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        n = scanner.nextInt();
        
        for (int i = 1; i <= n * n; i++) {
            clockwiseValues[i] = scanner.nextInt();
        }
        
        setOuterBoundaryVisited(); // 初始化矩阵外边界
        resetInnerVisited(); // 重置矩阵内部未访问
        fillClockwise(1, 1, 0, 1); // 顺时针填充矩阵
        
        setOuterBoundaryVisited(); // 重新设置矩阵外边界
        resetInnerVisited(); // 重置矩阵内部未访问
        outputCounterClockwise(1, 1, 0, 1); // 逆时针输出矩阵
    }
}
        题目内容
有一个N×N的矩阵,其中每个元素都是正整数,且1到N2的正整数恰好名出现一次。
可以将这个短阵按照顺时针螺施的方式组成一个链表:从左上角即(1,1)号格子出发,沿直线走到右上角即(1,N)号格子,再沿直线走到右下角即(N,N)号格子,再沿直线走到左下角即(N,1)号格子,再沿直线走到左上角下方即(2,1)号格子,这就完成了最外面的圈;随后从(2,1)号格子走到(2,2)号格子,接着完成里面的圈,以此类推,直到结束。
同样地,也可以将其按照逆时针螺旋的方式组成一个链表:从左上角即(1,1)号格子出发,沿直线走到左下角即(N,1)号格子,再沿直线走到有下角即(N,N)号格子,再沿直线走到右上角即(1,N)号格子,再沿直线走到左上角右方即(1,2)号格子,这就完成了最外面的圈,随后从(1,2)号格子走到(2,2)号格子,接看完成里面的圈,以此类推,直到结束。
下图给出了3 阶和4 阶矩阵的顺时针链表和逆时针链表的示例。
给定短阵的顺时针链表,求相应的逆时针链表。
输入描述
第一行包含一个整数 N,第二行包含 N2个整数,以空格分隔,表示顺时针链表中每个元素的值。
输出描述
含 N2个整数,以空格分隔,表示逆时针链表的每个元素的值。
样例1
输入
3
1 2 3 6 9 8 7 4 5
输出
1 4 7 8 9 6 3 2 5
说明
该矩阵如下:
1 2 3
4 5 6
7 8 9
样例2
输入
4
1 2 3 4 8 12 16 15 14 13 9 5 6 7 11 10
输出
1 5 9 13 14 15 16 12 8 4 3 2 6 10 11 7
说明
该矩阵如下:
1 2 3 4
5 6 7 8
9 10 11 12
13 14 15 16