CCF201412-2 就不老老实实做的Z字扫描!

问题描述
  在图像编码的算法中,需要将一个给定的方形矩阵进行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字形扫描的结果。


一般的Z字扫描思路就是规划、模拟呗:

1、正常前进:↙和↗两个方向,用一个方向向量轻松搞定

2、越界:分别判断四个界,强行置位到合法的新点上

可以略微增强性能的细节:如果使用递归,在正常进行时不开新分支,使用while直到不合法位置出现,找到的同时就进行打印

第一感觉写下的代码:

#include<iostream>
using namespace std;

#define MAX 500+5

int map[MAX][MAX];
int n, d, c, f=0;

const int dir[6][2] = { { 0, 1 }, { 0, -1 }, { 1, 0 }, { -1, 0 }, { -1, 2 }, { 2, -1 } };

bool in(int k){
	return k >= 0 && k < n;
}

int out(int x, int y){
	if (!in(x) && !in(y)){
		f = 1;
		return 5 - (n % 2);
	}
	else if (x < 0){
		return 2;
	}
	else if (x >= n){
		return 3+f*1;
	}
	else if (y < 0){
		return 0;
	}
	else{
		return 1+f*4;
	}
}

void zz(int x, int y){
	int dx = x, dy = y;
	bool fg = true;
	while (dx >= 0 && dx < n && dy >= 0 && dy < n){
		cout << map[dx][dy] << " ";
		if (dx == n - 1 && dy == n - 1) return;
		dx += d; dy -= d;
	}
	d = -1 * d;
	int a = out(dx, dy);
	dx += dir[a][0];
	dy += dir[a][1];
	zz(dx, dy);
}

int main(){
	cin >> n; d = 1; c = 0;
	for(int i = 0; i < n; i++){
		for (int j = 0; j < n; j++){
			cin >> map[j][i];
		}
	}
	zz(0, 0);
	return 0;
}

有点繁琐,因为刚开始对四个边界处理有点晕。


然后就满脑子都觉得这里肯定有规律!好吧大概是我从小到大就喜欢玩找规律...


其实仔细想想当知道当前点,下一个点的位置只有以下四种情况


上图中:黄色是↗方向,蓝色是↙方向,网格规格:N×N,图示为A点到B点间隔的步数


1、判断延伸方向:(x+y)%2 == 0即坐标和为偶数的点方向为↗

       (x+y)%2 == 1即坐标和为奇数的点方向为↙


2、判断延伸情况:通过min(y-1, N-x)或者min(x-1, N-y)即可得到延伸时最先遇到的是哪侧墙壁,结合延伸方向确定唯一情况。

3、获得相差步数:见图易知——同一方向两种情况步数差1,左上、右下情况步数 = 2×min【因为其实斜向长度和垂直边距是一样的】

4、每一行初始位置:每一行的某个数必定满足① 在其之前的数字都已经有了位置 ② 在其之后的数字位置不会比他靠前。所以每一行第一个数字的位置其实就是当前余下的空位中的首个位置。

上述四个条件都确定了之后可以直接在输入的时候就将对应的数字放到正确的位置:

#include<iostream>
using namespace std;

#define MAX (500+5)
#define min(a,b) (a<b?a:b)

int map[MAX * MAX];
int pos, n;

int step(int x, int y){
	int dt;
	if ((x + y) % 2 == 0){ // -↗-
		dt = (x - 1 >= n - y);
		return 2 * min((x - 1), (n - y)) + 1 - dt;
	}
	else{				   // -↙-
		dt = (y - 1 >= n - x);
		return 2 * min((y - 1), (n - x)) + 2 - dt;
	}
}


int main(){
	cin >> n;
	for(int i = 1; i <= n; i++){
		for (int z = 1; z <= n*n; z++){
			if (map[z] == 0){
				pos = z; break;
			}
		}
		for (int j = 1; j <= n; j++){
			cin >> map[pos];
			pos += step(i, j);
		}
	}
	for (int i = 1; i <= n*n; i++){
		cout << map[i] << " ";
	}
	cout << endl;
	return 0;
}

代码BUG:虽然题目没有值为0的情况出现,但这种思路实现的时候是0-key的方式,容易和数据中的0混淆,按道理讲应该将map初始化成INF比较合理。但既然题目数据为【正整数】就不强求了~


最后来看........................嗯效率并没有任何提高,和第一种方法的优化后结果差不多,甚至还比不上第一种。

但总会有些题可以找到规律,握着一种满分的代码就着急的去看下一个题...最终长进的,很有可能只是敲代码的手速。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值