题目
问题描述
在图像编码的算法中,需要将一个给定的方形矩阵进行Z字形扫描(Zigzag Scan)。给定一个n×n的矩阵,Z字形扫描的过程如下图所示:
对于下面的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的正整数。
对于一眼复杂且大的题目要考虑将其拆解成小问题并解决,比如题目中的箭头走向可能第一眼看上去无头绪,但是亲自体会箭头的走动后便可知此题目有规律可循。本题考察的是模拟
思路与注意项
-
箭头的下一步无非是4个方向,起点都是左上角到右下角。
-
观察箭头的转折点可以发现:
end就是走到了右下角,结束 -
向左下与向右上是主体部分,如果没有触碰到边界就沿此方向继续,触碰到就要判断下一步方向(根据第2步图,需要使用两个if判断),所以左下与右上需要使用循环while/for完成
-
画出n=2~5时的图,发现向左下的线条数为
int LeftDn = n-1
,向右上的线条数为:int RightUp = n-2
-
明确了遍历过程,那么设置开始时的位置,前两格是特殊的,会影响while内的遍历,故将其放在循环外面处理(注意:此时我遗漏了n=1的处理,导致第一次提交只有90分)
-
考虑是否需要处理输入数据的区间端点,发现n=1时与第5步的处理冲突,需要单独处理(直接输出即可)。
代码
#include <iostream>
//1≤n≤500
using namespace std;
int pic[501][501];
int n;
bool canDown(int i){
return ++i <= n;
}
bool canRight(int j){
return ++j <= n;
}
bool canLeftDn(int i,int j){
return ++i <= n && --j >= 1;
}
bool canRightUp(int i,int j){
return --i >= 1 && ++j <= n;
}
int main(){
cin >> n;
for(int i = 1;i <= n;i++){
for(int j = 1;j <= n;j++){
cin>>pic[i][j];
}
}
int LeftDn = n-1;
int RightUp = n-2;
int i=1;
int j=2;
//i=1,j=2是因为要从第一行第二列开始进入while循环
if(n == 1){
cout << pic[1][1];return 0;
}
cout<<pic[1][1]<<" "<<pic[1][2]<<" ";
//循环终止条件为是否到达右下角
while(!(i==n && j==n)){
if(LeftDn){
while(canLeftDn(i,j)){
i++;j--;
cout << pic[i][j] << " ";
}
LeftDn--;
//这里注意向下和向右的优先级,能下就下,不能再右
if(canDown(i)){
i++;
cout << pic[i][j] << " ";
}
else if(canRight(j)){
j++;
cout << pic[i][j] << " ";
}
}
//原理同向左上,注意向下和向右的优先级
if(RightUp){
while(canRightUp(i,j)){
i--;j++;
cout << pic[i][j] << " ";
}
RightUp--;
if(canRight(j)){
j++;
cout << pic[i][j] << " ";
}
else if(canDown(i)){
i++;
cout << pic[i][j] << " ";
}
}
}
return 0;
}