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比較合理。但既然題目數據爲【正整數】就不強求了~


最後來看........................嗯效率並沒有任何提高,和第一種方法的優化後結果差不多,甚至還比不上第一種。

但總會有些題可以找到規律,握着一種滿分的代碼就着急的去看下一個題...最終長進的,很有可能只是敲代碼的手速。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章