1. 程式人生 > >程式設計能力提高------蛇形填數(方塊填數+三角形填數)

程式設計能力提高------蛇形填數(方塊填數+三角形填數)

回憶曾經:

考研複試上機做題時,遇到過一次這個蛇形填數問題,當時不會做;後來又遇到一個蛇形填充(基於三角形),也不會做;這讓我下定決心,一定要找到這兩個問題的解。後來才知道,這種問題,是學習ACM的入門問題。

蛇形填數(一)蛇形矩陣


問題一:描述 

以下內容參考《演算法競賽入門經典(第2版)劉汝佳》

在n*n方陣裡填入1,2,„,n*n,要求填成蛇形。例如n=4時方陣為 

10    11   12   1 

  9    16   13   2 

  8    15   14   3

  7     6     5    4  

上面的方陣中,多餘的空格只是為了便於觀察規律,不必嚴格輸出。n≤8。

問題分析:

      字元的特殊圖形輸出問題,我們暫時可以分為2類。
第一類是有一定對稱性的幾何圖形,比如說列印倒三角形或者菱形等。這種題目一般思路就是找出圖形的特點(對稱性等)與迴圈變數(行號,列號)之間的關係。我們可以假設行用i表示,列用j表示。我們的目的就是找出i,j與圖形之間的對應關係。按圖形形狀的不同,i和j之間的複雜性不同。但是都可以看做是在尋找一種或多種"靜態關係"。 
 第二類是有一定規律性的圖形,比如蛇形填數,走棋盤等。這種題目的一般思路就是找出題目中對圖形的限制條件(不能出界,按照一定規則填充等)。我們首先要通過觀察理解用到的“規則”,然後用各種迴圈和if語句將這些"規則"變成程式語句。同樣,根據"規則"不同,複雜性也不同。但是都可以看做是在尋找一種或多種i和j之間的"動態關係"。 


      通過觀察,我們可以看出規則:在規定的方陣n*n裡,在不越界及不走重複位置的前提下,填充元素遵循右下左上的規定.即向右走到頂後向下走到頂,再向左走到頂,向上走到頂。

      類比數學中的矩陣,可以用一個二維陣列來儲存題目中的方陣。只要宣告一個“int  a[maxn][maxn]”,就可以獲得一個大小為maxn*maxn的方陣,在宣告時,二維的大小不必相同,因此也可以宣告int a[30][50]這樣的陣列。

      從1開始依次填寫。設“筆”的座標為(x,y),則一開始x=0,y=n-1,即第0行,第n-1列。“筆”的移動軌跡是:下,下,下,左,左,左,上,上,上,右,右,下,下,左,上。總之,先是下,到不能填為止,然後是左,接著是上,最後是右。“不能填”是指再走就出界(例如4→5),或者再走就要走到以前填過的格子(例如12→13)。如果把所有格子初始化為0,就能很方便地加以判斷。

在這裡我們就要提到在很多時候都要用到的判斷方法"預判".即提前一格判斷下一格是否越界,如果下一格越界就不再移動。

#include <stdio.h>
#include <string.h>
#define MAXN 20
int a[MAXN][MAXN];//陣列空間分配在main內外,有點區別

int main()
{
  int n, x, y, count = 1;
  scanf("%d", &n);
  memset(a, 0, sizeof(a));//用於判斷格子是否被填數,memset可以初始化二維陣列。
  x = 0; y = n-1;
  a[x][y] = 1;
  while(count < n*n)
  {
     while(x+1<n && !a[x+1][y]) a[++x][y] = ++count; //先判斷越界,再判斷格子是否填過數 
     while(y-1>=0  && !a[x][y-1]) a[x][--y] = ++count;
     while(x-1>=0  && !a[x-1][y]) a[--x][y] = ++count;
     while(y+1<n && !a[x][y+1]) a[x][++y] = ++count;     
  }
  for(x = 0; x < n; x++)
  {
     for(y = 0; y < n; y++)
        printf("%3d", a[x][y]);
     printf("\n");
  }
  system("PAUSE");	
  return 0;
}

        4條while語句有些難懂,其實是有原則的。先判斷,再行走,而不是走一步後發現越界再退回來。這樣則需要進行“預判”,即是否越界,以及如果繼續往下走會不會到達一個已經填過的格子。越界只需判斷x+1<n,因為y的值並沒有修改;下一個格子是(x+1,y),因此需要判斷a[x+1][y]==0,簡寫成 “!a[x+1][y] ”。

       實現方式有多種。如果在程式設計時,先移動位置再判斷是否合適。如果發現越界再退回來一步,無需提前進行預判是否越界或是否已填過。實現方式,見下面。

#include <stdio.h>
#include <string.h>
#define MAXN 20
int arr[MAXN][MAXN];//陣列空間分配在main內外,有點區別

int main() {
    int n, i, j, count = 1;
    printf("請輸入方陣的寬頻n:");
    scanf("%d", &n);
    memset(arr, 0, sizeof(arr));//用於判斷格子是否被填數,memset可以初始化二維陣列。
    i = 0;      //行標
    j = n-1;    //列標
    while(count <= n*n) {


        while(arr[i][j] == 0 && i < n)
            arr[i++][j] = count ++;
        i --;                           //回退一格
        j --;

        while(arr[i][j] == 0 && j >= 0)
            arr[i][j --] = count ++;
        j ++;                           //回退一格
        i --;

        while(arr[i][j] == 0 && i >= 0)
            arr[i --][j] = count ++;
        i ++;                           //回退一格
        j ++;
        while(arr[i][j] == 0 && j < n)
            arr[i][j ++] = count ++;
        j --;                           //回退一格
        i ++;
    }

    printf("輸出方陣為:\n");
    for(i = 0; i < n; i++) {
        for(j = 0; j < n; j++)
            printf("%3d", arr[i][j]);
        printf("\n");
    }
    system("PAUSE");
    return 0;
}


蛇形填數(二)蛇形三角


問題二:描述 

1    2   3  4  5
12  13  14  6
11  15  7
10  8
9
跟蛇形填數一樣,只是填數要求按照三角形填。
輸入
第一行有一個N,表示N組測試資料
接下來每組資料包括一個數字X,表示三角形的邊長,0< X <1000
輸出
輸出填好之後的圖
樣例輸入
2
5
4
樣例輸出
1  2  3  4  5
12 13 14 6
11 15 7
10 8
9

1  2  3  4
9  10 5
8  6
7
#include <iostream>
#include <string.h>
using namespace std;
const int MAXN = 100;
int data[MAXN][MAXN];
int main() {
    int n,length;
    cin>>n;
    while(n--) {
        memset(data,0,sizeof(data));
        cin>>length;
        int x=0,y=0,cnt=1;
        data[x][y]=cnt;
        while(cnt<((length+1)*length)/2) {
            while ((y+1<length-x) && !data[x][y+1])
                data[x][++y]=++cnt;
            while ((x+1<length) && (y-1)>-1 &&!data[1+x][y-1] )
                data[++x][--y]=++cnt;
            while (x-1>-1 && (!data[x-1][y]))
                data[--x][y]=++cnt;
        }
        for (int i=0; i< length ; i++) {
            for (int j=0; j<length-i; ++j) {
                cout<<data[i][j]<<"  ";
            }
            cout<<endl;
        }
        cout<<endl;
    }
    return 0;
}