>题目
在图像编码的算法中,需要将一个给定的方形矩阵进行Z字形扫描(Zigzag Scan)。给定一个m×n的矩阵,Z字形扫描的过程如下图所示。
对于下面给出的4×4的矩阵和3×5的矩阵:
0 5 2 9 1 2 6 7 7
7 7 5 6 3 5 8 8 6
9 5 6 9 4 9 9 5 4
7 3 0 5
对其进行Z字形扫描后得到长度为16和15的序列:
0 5 7 9 7 2 9 5 5 7 3 6 6 9 0 5
1 2 3 4 5 6 7 8 9 9 8 7 6 5 4
请实现一个Z字形扫描的程序,给定一个m×n的矩阵,输出对这个矩阵进行Z字形扫描的结果。
>输入数据
第一行为一个整数N,表示共有几组测试数据。
接下来是N组数据,每组数据的第一行为两个整数m,n,分别为矩阵的行和列数;接下来的m行数据,分别为n个整数值,即矩阵的值,如:
2
4 5
1 2 6 7 14
3 5 8 13 15
4 9 12 16 19
10 11 17 18 20
3 5
1 2 6 7 12
3 5 8 11 13
4 9 10 14 15
>输出格式
输出 (行号,列号):[值] ,如对以上两组中的第一组测试数据,输出如下:
(0,0):[1]
(0,1):[2]
(1,0):[3]
(2,0):[4]
….(此处省略部分内容)….
(2,4):[19]
(3,4):[20]
>题解
更新:这里有一种更好理解的解法:
http://blog.csdn.net/Shenpibaipao/article/details/78877294
———————————-
我们设置一个游标cursor,其值x和y分别为当前游标在矩阵中的行号和列号。每次输出完游标指向的矩阵中的值之后,就把游标向应当移动的地方移动,直到游标指向最后一个矩阵值:
while (matrix[cursor]不是最后一个矩阵值){
print:matirx[cursor]; // 输出当前值
offset = getNextOffset(); // 判断移动方向
cursor += offset; // 移动游标
}
其中移动方向只有四个,东、东北、南、西南:
定义一个结构体,直接把这四个方向对游标的偏移量标出来:
struct Offset{
int off_x,off_y;
};
// East Southwest South Northeast
Offset E={0,1}, SW={1,-1}, S={1,0}, NE={-1,1};
把矩阵的四个边缘从北开始,顺时针标注为(N)、(E)、(S)、(W)。当游标以某个方向
进入该边缘或在该边缘内移动时,即发生了“
碰撞”,需要改变方向;否则,继续以之前的方向移动。
接下来,就是判断游标可能以哪几种方向“碰撞”该边缘,然后给定碰撞后产生的新方向:
// (边缘) : 碰撞前的方向->(碰撞的边缘)->碰撞后产生的方向 ;其中,加 ' 的方向指的是可能存在但实际上不可能发生情况
(S) : E/S->(S)->NE or NE'/SW->(S)->E
(E) : E/S->(E)->SW or NE/SW'->(E)->S
(N) : SW'/E->(N)->SW or NE/S'->(N)->E
(W) : NE'/S->(W)->NE or E'/SW->(W)->S
需要注意的是,当游标进入边角的点时,会同时碰撞两个边缘。碰撞时的优先级分析如下:(S)=(E) > (N)=(W)。其中(S)=(E)和(N)=(W)是因为(S)(E)交点是矩阵最后一个点,(S)(E)交点是矩阵起点,分别为循环开始和退出的标志,不要考虑优先级,因此他们等价同级;(S)、(E)>(N)、(W)是因为,(S)和(E)永远都是从(N)和(W)进入的,等于说一旦触发到(S)(W)交点和(N)(E)交点时,实际上其真正进入的是(S)和(E)而非(N)和(W),哪怕这个点同时属于两条边。所以,这个逻辑应当这么写:
if(cx == maxX-1) return isSameOffset(cp,SW) ? E : NE; // 先判断是否进入(S)
else if(cy == maxY-1) return isSameOffset(cp,NE) ? S : SW; // 后判断是否进入(E)
else if(cx == 0) return isSameOffset(cp,NE) ? E : SW; // 接着判断是否进入(N)
else if(cy == 0) return isSameOffset(cp,SW) ? S : NE; // 最后判断是否进入(W)
最后,还需要注意的是:只有一行和一列的数据,此时分别只会向东和向南移动。
>完整代码(含注释)
#include <iostream>
using namespace std;
struct Offset{
int off_x,off_y;
};
bool isSameOffset(Offset a, Offset b){
return a.off_x == b.off_x && a.off_y == b.off_y;
}
// East Southwest South Northeast
Offset E={0,1}, SW={1,-1}, S={1,0}, NE={-1,1};
Offset changeOffset(int cx, int cy ,Offset cp,int maxX, int maxY){
if(maxX==1) return E;
else if(maxY==1) return S;
// matrix-wall collision priority : (S)=(E) > (N)=(W)
// (S) : E/S->(S)->NE or NE'/SW->(S)->E
else if(cx == maxX-1) return isSameOffset(cp,SW) ? E : NE;
// (E) : E/S->(E)->SW or NE/SW'->(E)->S
else if(cy == maxY-1) return isSameOffset(cp,NE) ? S : SW;
// (N) : SW'/E->(N)->SW or NE/S'->(N)->E
else if(cx == 0) return isSameOffset(cp,NE) ? E : SW;
// (W) : NE'/S->(W)->NE or E'/SW->(W)->S
else if(cy == 0) return isSameOffset(cp,SW) ? S : NE;
else return cp;
}
void z_ScanMatrix(int m, int n){
int matrix[m][n];
// readIn data
for(int i=0;i<m;i++){
for(int j=0;j<n;j++){
cin>>matrix[i][j];
}
}
// cursor at beginning : (0,0)
int cx=0,cy=0;
// movement state at the scan begining : To NorthEast
Offset cp = NE;
// Zigzag Scan :
while(cx!=m-1 || cy!=n-1){
// output
cout<<"("<<cx<<","<<cy<<"):["<<matrix[cx][cy]<<"]"<<endl;
// judge which way to go
cp = changeOffset(cx,cy,cp,m,n);
// move
cx += cp.off_x; cy += cp.off_y;
}
// output the end-point's value
cout<<"("<<cx<<","<<cy<<"):["<<matrix[cx][cy]<<"]"<<endl;
}
int main(){
// N : numbers of test-data files
// matrix[m,n]
int m,n,N;
cin>>N;
while(N-->0){
cin>>m>>n;
z_ScanMatrix(m,n);
}
return 0;
}
附带一些测试数据:
/*
7
4 5
1 2 6 7 14
3 5 8 13 15
4 9 12 16 19
10 11 17 18 20
3 5
1 2 6 7 12
3 5 8 11 13
4 9 10 14 15
3 3
1 2 6
3 5 7
4 8 9
4 4
1 2 6 7
3 5 8 13
4 9 12 14
10 11 15 16
1 3
1 2 3
4 1
1
2
3
4
1 1
1
*/