Python入门习题(31)——CCF CSP认证考试真题:Z字形扫描

CCF CSP真题讲解--Z字形扫描

  • 题目描述
  • 第一种解法
    • 解题思路
    • 参考答案
  • 第二种解法
    • 解题思路
    • 参考答案
  • 测试用例
  • 小结

题目描述

试题编号: 201412-2
试题名称: Z字形扫描
时间限制: 2.0s
内存限制: 256.0MB
问题描述
  在图像编码的算法中,需要将一个给定的方形矩阵进行Z字形扫描(Zigzag Scan)。给定一个n×n的矩阵,Z字形扫描的过程如下图所示:
Python入门习题(31)——CCF CSP认证考试真题:Z字形扫描_第1张图片
  对于下面的4×4的矩阵,
  1 5 3 9
  3 7 5 6
  9 4 6 4
  7 3 1 3
  对其进行Z字形扫描后得到长度为16的序列:
  1 5 3 9 7 3 9 5 4 7 3 6 6 4 1 3
  请实现一个Z字形扫描的程序,给定一个n×n的矩阵,输出对这个矩阵进行Z字形扫描的结果。
输入格式
  输入的第一行包含一个整数n,表示矩阵的大小。
  输入的第二行到第n+1行每行包含n个正整数,由空格分隔,表示给定的矩阵。
输出格式
  输出一行,包含n×n个整数,由空格分隔,表示输入的矩阵经过Z字形扫描后的结果。
样例输入
4
1 5 3 9
3 7 5 6
9 4 6 4
7 3 1 3
样例输出
1 5 3 9 7 3 9 5 4 7 3 6 6 4 1 3
评测用例规模与约定
  1≤n≤500,矩阵元素为不超过1000的正整数。

第一种解法

解题思路

一、结合横坐标和纵坐标来看,z字形扫描是有规律的。
以4*4方阵为例:
Python入门习题(31)——CCF CSP认证考试真题:Z字形扫描_第2张图片

  1. 起点是(0, 0)位置。
  2. 斜向下扫描。经过的每一个单元格,横坐标+纵坐标=1。纵坐标从1,逐步减1,直至0。
  3. 斜向上扫描。经过的每一个单元格,横坐标+纵坐标=2。横坐标从2,逐步减1,直至0。
  4. 斜向下扫描。经过的每一个单元格,横坐标+纵坐标=3。纵坐标从3,逐步减1,直至0。
  5. 斜向上扫描。经过的每一个单元格,横坐标+纵坐标=4。横坐标从3,逐步减1,直至1。
  6. 斜向下扫描。经过的每一个单元格,横坐标+纵坐标=5。纵坐标从3,逐步减1,直至2。
  7. 斜向上扫描。经过的每一个单元格,横坐标+纵坐标=6。横坐标从3,逐步减1,直至3。(实际上只扫描一次)

二、进一步地,z字形扫描的规律可表述为:
(1)斜向下扫描时,纵坐标从min(t, n-1),逐步减1,直至t - min(t, n-1)。这里,t为该扫描线任意单元格横坐标加上纵坐标之和。min(t, n-1)是取t和n-1的最小值的意思。
(2)斜向上扫描时,横坐标从min(t, n-1),逐步减1,直至t - min(t, n-1)。这里,t为该扫描线任意单元格横坐标加上纵坐标之和。
(3)t为奇数,是斜向下扫描。t为偶数,是斜向上扫描。

参考答案


n = int(input())
n_mtx = []
for i in range(n):
    n_line = [int(s) for s in input().split()]
    n_mtx.append(n_line)
# print(n_mtx)

print(n_mtx[0][0], end = ' ')
max_xplusy = n - 1 + n - 1  #横坐标+纵坐标之和的最大值,即右下角元素的横坐标+纵坐标之和

#沿斜线迭代n*n方阵mtx的元素,这些元素的横坐标+纵坐标=t
def itr(mtx, n, t):
    start = min(n-1, t)
    if t % 2 == 0:  #偶数,斜向上遍历
        for x in range(start, t - start - 1, -1):
            print(mtx[x][t-x], end = ' ')
    else:  #t为奇数,斜向下遍历
        for y in range(start, t - start - 1, -1):
            print(mtx[t-y][y], end = ' ')

for t in range(1, max_xplusy + 1):
    #遍历斜线上的元素
    itr(n_mtx, n, t)

第二种解法

第二种解法费劲些,是我以前编写的。我保留该解法以供对比。你可以看到,第一种解法的思路更清晰,代码更简洁,引发错误的环节更少。

解题思路

  1. 扫描从左上角出发,首先是右拐,接下来扫描的顺序是: 沿左下移动 --> 拐弯 --> 沿右上移动 --> 拐弯 --> 沿左下移动 -->… (不断重复)。什么时候要拐弯?答案是当移动到矩阵边界的时候。
  2. 拐弯有两种,一是向右,二是向下。什么时候向右?答案是移动到矩阵顶部或底部的时候。什么时候向下?答案是移动到矩阵左边界或右边界的时候。特别注意,移动到右上角元素的时候,要向下拐弯。移动到左下角元素的时候,要向右拐弯。

参考答案

def turn(row, column, n_size):
'''拐弯的处理'''
    if row == n_size - 1:   #已到达底部
        column += 1
    elif column == n_size - 1:  #已到达右边界
        row += 1
    elif row == 0:    #已到达顶部(须放在‘已到达右边界’之后)
        column += 1
    elif column == 0:  #已到达左边界(须放在‘已到达底部’之后)
        row += 1
    return (row, column)

def move_left_down(row, column, n_size):
'''向左下移动一格。到达边界的话,则拐弯。'''
    if column == 0 or row == n_size - 1:
        row, column = turn(row, column, n_size)
        return (row, column, 1)
    else:
        row += 1
        column -= 1
        return (row, column, 0)

def move_right_up(row, column, n_size):
'''向左上移动一格。到达边界的话,则拐弯。'''
    if row == 0 or column == n_size - 1:
        row, column = turn(row, column, n_size)
        return (row, column, 0)
    else:
        row -= 1
        column += 1
        return (row, column, 1)

#输入矩阵
n = int(input())
n_mtx = []
for i in range(n):
    n_line = [int(s) for s in input().split()]
    n_mtx.append(n_line)
# print(n_mtx)

#打印左上角元素
print(n_mtx[0][0], end = ' ')
row = 0
column = 1    
move_index = 0  #0: left_down, 1: right_up。 
 #依次打印扫描到的元素
while row < n and column < n:
    print(n_mtx[row][column], end = ' ')  
    if move_index == 0:              #0与1交替,决定了扫描移动的方向
        row, column, move_index = move_left_down(row, column, n)
    else:
        row, column, move_index = move_right_up(row, column, n)

代码有些长,有没有更加简洁的思路。如果读者找到更加简洁的思路,请在留言中指出。

测试用例

  1. n为偶数的情形。比如4x4的矩阵。题目描述给出的测试用例覆盖了本情形。
  2. n为奇数的情形。比如5x5的矩阵。
    样例输入
    3
    1 2 6
    3 5 7
    4 8 9
    样例输出
    1 2 3 4 5 6 7 8 9
  3. n为1的情形。不要忘了这种极端情形。
    样例输入
    1
    5
    样例输出
    5

小结

  1. 第一种解法的代码明显比第二种解法的代码短。在编程的时候,尽量找到简洁的算法。
  2. 本题,n为1的情形是一个坑。

你可能感兴趣的:(Python编程,Python入门100道习题)