軟考常用算法設(shè)計(jì)方法(一)(5)

字號(hào):

【問題】 n皇后問題
    問題描述:求出在一個(gè)n×n的棋盤上,放置n個(gè)不能互相捕捉的國際象棋“皇后”的所有布局。
     這是來源于國際象棋的一個(gè)問題?;屎罂梢匝刂v橫和兩條斜線4個(gè)方向相互捕捉。如圖所示,一個(gè)皇后放在棋盤的第4行第3列位置上,則棋盤上凡打“×”的位置上的皇后就能與這個(gè)皇后相互捕捉。
    1 2 3 4 5 6 7 8
     × ×
    × × ×
     × × ×
    × × q × × × × ×
     × × ×
    × × ×
     × ×
     × ×
    從圖中可以得到以下啟示:一個(gè)合適的解應(yīng)是在每列、每行上只有一個(gè)皇后,且一條斜線上也只有一個(gè)皇后。
     求解過程從空配置開始。在第1列至第m列為合理配置的基礎(chǔ)上,再配置第m+1列,直至第n列配置也是合理時(shí),就找到了一個(gè)解。接著改變第n列配置,希望獲得下一個(gè)解。另外,在任一列上,可能有n種配置。開始時(shí)配置在第1行,以后改變時(shí),順次選擇第2行、第3行、…、直到第n行。當(dāng)?shù)趎行配置也找不到一個(gè)合理的配置時(shí),就要回溯,去改變前一列的配置。得到求解皇后問題的算法如下:
     { 輸入棋盤大小值n;
     m=0;
     good=1;
     do {
     if (good)
     if (m==n)
     { 輸出解;
     改變之,形成下一個(gè)候選解;
     }
     else 擴(kuò)展當(dāng)前候選接至下一列;
     else 改變之,形成下一個(gè)候選解;
     good=檢查當(dāng)前候選解的合理性;
     } while (m!=0);
     }
     在編寫程序之前,先確定邊式棋盤的數(shù)據(jù)結(jié)構(gòu)。比較直觀的方法是采用一個(gè)二維數(shù)組,但仔細(xì)觀察就會(huì)發(fā)現(xiàn),這種表示方法給調(diào)整候選解及檢查其合理性帶來困難。更好的方法乃是盡可能直接表示那些常用的信息。對(duì)于本題來說,“常用信息”并不是皇后的具體位置,而是“一個(gè)皇后是否已經(jīng)在某行和某條斜線合理地安置好了”。因在某一列上恰好放一個(gè)皇后,引入一個(gè)一維數(shù)組(col[ ]),值col[i]表示在棋盤第i列、col[i]行有一個(gè)皇后。例如:col [3]=4,就表示在棋盤的第3列、第4行上有一個(gè)皇后。另外,為了使程序在找完了全部解后回溯到最初位置,設(shè)定col[0]的初值為0當(dāng)回溯到第0列時(shí),說明程序已求得全部解,結(jié)束程序運(yùn)行。
     為使程序在檢查皇后配置的合理性方面簡(jiǎn)易方便,引入以下三個(gè)工作數(shù)組:
    (1) 數(shù)組a[ ],a[k]表示第k行上還沒有皇后;
    (2) 數(shù)組b[ ],b[k]表示第k列右高左低斜線上沒有皇后;
    (3) 數(shù)組 c[ ],c[k]表示第k列左高右低斜線上沒有皇后;
    棋盤中同一右高左低斜線上的方格,他們的行號(hào)與列號(hào)之和相同;同一左高右低斜線上的方格,他們的行號(hào)與列號(hào)之差均相同。
     初始時(shí),所有行和斜線上均沒有皇后,從第1列的第1行配置第一個(gè)皇后開始,在第m列col[m]行放置了一個(gè)合理的皇后后,準(zhǔn)備考察第m+1列時(shí),在數(shù)組a[ ]、b[ ]和c[ ]中為第m列,col[m]行的位置設(shè)定有皇后標(biāo)志;當(dāng)從第m列回溯到第m-1列,并準(zhǔn)備調(diào)整第m-1列的皇后配置時(shí),清除在數(shù)組a[ ]、b[ ]和c[ ]中設(shè)置的關(guān)于第m-1列,col[m-1]行有皇后的標(biāo)志。一個(gè)皇后在m列,col[m]行方格內(nèi)配置是合理的,由數(shù)組a[ ]、b[ ]和c[ ]對(duì)應(yīng)位置的值都為1來確定。細(xì)節(jié)見以下程序:
    【程序】
    # include
    # include
    # define maxn 20
    int n,m,good;
    int col[maxn+1],a[maxn+1],b[2*maxn+1],c[2*maxn+1];
    void main()
    { int j;
     char awn;
     printf(“enter n: “); scanf(“%d”,&n);
     for (j=0;j<=n;j++) a[j]=1;
     for (j=0;j<=2*n;j++) cb[j]=c[j]=1;
     m=1; col[1]=1; good=1; col[0]=0;
     do {
     if (good)
     if (m==n)
     { printf(“列\(zhòng)t行”);
     for (j=1;j<=n;j++)
     printf(“%3d\t%d\n”,j,col[j]);
     printf(“enter a character (q/q for exit)!\n”);
     scanf(“%c”,&awn);
     if (awn==’q’||awn==’q’) exit(0);
     while (col[m]==n)
     { m--;
     a[col[m]]=b[m+col[m]]=c[n+m-col[m]]=1;
     }
     col[m]++;
     }
     else
     { a[col[m]]=b[m+col[m]]=c[n+m-col[m]]=0;
     col[++m]=1;
     }
     else
     { while (col[m]==n)
     { m--;
     a[col[m]]=b[m+col[m]]=c[n+m-col[m]]=1;
     }
     col[m]++;
     }
     good=a[col[m]]&&b[m+col[m]]&&c[n+m-col[m]];
     } while (m!=0);
    }