1. 程式人生 > 實用技巧 >【CCF201412-2】Z字形掃描

【CCF201412-2】Z字形掃描

試題編號:

201412-2

試題名稱:

Z字形掃描

時間限制:

2.0s

記憶體限制:

256.0MB

問題描述

  在影象編碼的演算法中,需要將一個給定的方形矩陣進行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字形掃描的結果。

輸入格式

  輸入的第一行包含一個整數n,表示矩陣的大小。
  輸入的第二行到第n+1行每行包含n個正整數,由空格分隔,表示給定的矩陣。

輸出格式

  輸出一行,包含n×n個整數,由空格分隔,表示輸入的矩陣經過Z字形掃描後的結果。

樣例輸入

4
1 5 3 9
3 7 5 6
9 4 6 4
7 3 1 3

樣例輸出

1 5 3 9 7 3 9 5 4 7 3 6 6 4 1 3

評測用例規模與約定

1≤n≤500,矩陣元素為不超過1000的正整數。

解題報告

題意

給定n×n的矩陣,從左上角開始,按左下右上—右上左下的規律遍歷。

思路

一個n×n的矩陣有2n-1條斜線,本題其實就是按照斜線遍歷矩陣

我們從左上到右下對2n-1條斜線標號,那麼遍歷矩陣時,標號為奇數的斜線從左下到右上遍歷,標號為偶數的矩陣從右上到左下遍歷

以樣例為例,遍歷過程如下圖:

模擬矩陣的遍歷,用i,j表示遍歷到矩陣的第i行第j列,即(i,j)

初始時令i=1,j=1,即從矩陣第一條斜線的第一個數開始

由於這條斜線只有一個數,可以直接輸出

然後從第二條斜線開始,把剩下2n-2條斜線分組,相鄰兩條斜線為一組,每一組先遍歷標號為偶數的斜線再遍歷標號為奇數的斜線,即,每一組先從右上到左下遍歷,再從左下到右上遍歷

對於右上到左下遍歷,下一個位置為(i+1,j-1)

對於左下到右上遍歷,下一個位置為(i-1,j+1)

接下來考慮遍歷完一條斜線後開始遍歷下一條斜線時的過渡

我們注意到,在對角線(即第n條斜線)之前,

標號為奇數的斜線向標號為偶數的斜線過渡時,標號為奇數的斜線的最後一個數在標號為偶數的斜線的第一個數的正左方;

標號為偶數的斜線向標號為奇數的斜線過渡時,標號為偶數的斜線的最後一個數在標號為奇數的斜線的第一個數的正上方

於是我們得到相鄰兩條斜線過渡的演算法

若(i,j)是標號為奇數的斜線的最後一個數,則(i,j+1)過渡到下一條斜線

若(i,j)是標號為偶數的斜線的最後一個數,則(i+1,j)過渡到下一條斜線

同理,我們可以得到對角線後的過渡演算法

若(i,j)是標號為奇數的斜線的最後一個數,則(i+1,j)過渡到下一條斜線

若(i,j)是標號為偶數的斜線的最後一個數,則(i,j+1)過渡到下一條斜線

 1 #include <cstdio>
 2 int n,a[505][505];
 3 int main() 
 4 {
 5     int i,j;
 6     scanf("%d",&n);
 7     for (i=1;i<=n;i++)
 8       for (j=1;j<=n;j++)
 9         scanf("%d",&a[i][j]);
10     printf("%d",a[1][1]);
11     //  直接輸出第一個數 
12     i=j=1;  //  初始化 
13     while (i>=1 && i<=n && j>=1 && j<=n)
14     //  確保遍歷的位置在矩陣之內 
15     //  一次迴圈為一組遍歷 
16     {
17         //  遍歷標號為偶數的斜線 
18         if (j<n)  //  當前斜線在對角線之前 
19           j++;
20         else      //  當前斜線在對角線之後 
21           i++;
22         if (i>n) break;  
23         //  若已經遍歷到(n,n)則退出迴圈
24         //  由於只有當j==n時才會執行i++
25         //  所以當i>n時j一定已經達到n 
26         printf(" %d",a[i][j]);  //  輸出第一個數 
27         
28         while (j>1 && i<n) 
29         //  遍歷一條斜線 
30         {
31             i++;   j--;
32             printf(" %d",a[i][j]);
33         }
34         
35         //  遍歷編號為奇數的斜線 
36         if (i<n)  //  當前斜線在對角線之前
37           i++;
38         else     //  當前斜線在對角線之後 
39           j++;
40         if (j>n) break;
41         printf(" %d",a[i][j]);
42         while (i>1 && j<n)
43         {
44             i--;   j++;
45             printf(" %d",a[i][j]);
46         }
47           
48     }
49     return 0;
50 }