>题目
在图像编码的算法中,需要将一个给定的方形矩阵进行Z字形扫描(Zigzag Scan)。给定一个m×n的矩阵,Z字形扫描的过程如下图所示。
对于下面给出的4×4的矩阵和3x5的矩阵:
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 */