[C/C++]OJ练习题:Z字形扫描(Zigzag Scan)

>题目

    在图像编码的算法中,需要将一个给定的方形矩阵进行Z字形扫描(Zigzag Scan)。给定一个m×n的矩阵,Z字形扫描的过程如下图所示。 

《[C/C++]OJ练习题:Z字形扫描(Zigzag Scan)》

    对于下面给出的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; // 移动游标
}

     其中移动方向只有四个,东、东北、南、西南:

《[C/C++]OJ练习题:Z字形扫描(Zigzag Scan)》

     定义一个结构体,直接把这四个方向对游标的偏移量标出来:

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
*/

    原文作者:Z字形编排问题
    原文地址: https://blog.csdn.net/Shenpibaipao/article/details/78863451
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞