皇后問題

      網(wǎng)友投稿 640 2025-04-01

      八皇后問題是一個以國際象棋為背景的問題:如何能夠在 8×8 的國際象棋棋盤上放置八個皇后,使得任何一個皇后都無法直接吃掉其他的皇后?為了達(dá)到此目的,任兩個皇后都不能處于同一條橫行、縱行或斜線上。八皇后問題可以推廣為更一般的n皇后擺放問題:這時棋盤的大小變?yōu)閚1×n1,而皇后個數(shù)也變成n2。而且僅當(dāng) n2 ≥ 1 或 n1 ≥ 4 時問題有解。

      皇后問題是非常著名的問題,作為一個棋盤類問題,毫無疑問,用暴力搜索的方法來做是一定可以得到正確答案的,但在有限的運(yùn)行時間內(nèi),我們很難寫出速度可以忍受的搜索,部分棋盤問題的最優(yōu)解不是搜索,而是動態(tài)規(guī)劃,某些棋盤問題也很適合作為狀態(tài)壓縮思想的解釋例題。

      進(jìn)一步說,皇后問題可以用人工智能相關(guān)算法和遺傳算法求解,可以用多線程技術(shù)縮短運(yùn)行時間。本文不做討論。

      (本文不展開講狀態(tài)壓縮,以后再說)

      皇后問題

      一般思路:

      N*N的二維數(shù)組,在每一個位置進(jìn)行嘗試,在當(dāng)前位置上判斷是否滿足放置皇后的條件(這一點(diǎn)的行、列、對角線上,沒有皇后)。

      優(yōu)化1:

      既然知道多個皇后不能在同一行,我們何必要在同一行的不同位置放多個來嘗試呢?

      我們生成一維數(shù)組record,record[i]表示第i行的皇后放在了第幾列。對于每一行,確定當(dāng)前record值即可,因?yàn)槊啃兄荒芮冶仨毞乓粋€皇后,放了一個就無需繼續(xù)嘗試。那么對于當(dāng)前的record[i],查看record[0...i-1]的值,是否有j = record[k](同列)、|record[k] - j| = | k-i |(同一斜線)的情況。由于我們的策略,無需檢查行(每行只放一個)。

      public class NQueens {

      public static int num1(int n) {

      if (n < 1) {

      return 0;

      }

      int[] record = new int[n];

      return process1(0, record, n);

      }

      public static int process1(int i, int[] record, int n) {

      if (i == n) {

      return 1;

      }

      int res = 0;

      for (int j = 0; j < n; j++) {

      if (isValid(record, i, j)) {

      record[i] = j;

      res += process1(i + 1, record, n);

      }

      }//對于當(dāng)前行,依次嘗試每列

      return res;

      }

      //判斷當(dāng)前位置是否可以放置

      public static boolean isValid(int[] record, int i, int j) {

      for (int k = 0; k < i; k++) {

      if (j == record[k] || Math.abs(record[k] - j) == Math.abs(i - k)) {

      return false;

      }

      }

      return true;

      }

      public static void main(String[] args) {

      int n = 8;

      System.out.println(num1(n));

      }

      }

      優(yōu)化2:

      分析:棋子對后續(xù)過程的影響范圍:本行、本列、左右斜線。

      黑色棋子影響區(qū)域?yàn)榧t色

      本行影響不提,根據(jù)優(yōu)化一已經(jīng)避免

      本列影響,一直影響D列,直到第一行在D放棋子的所有情況結(jié)束。

      左斜線:每向下一行,實(shí)際上對當(dāng)前行的影響區(qū)域就向左移動

      比如:

      嘗試第二行時,黑色棋子影響的是我們的第三列;

      嘗試第三行時,黑色棋子影響的是我們的第二列;

      嘗試第四行時,黑色棋子影響的是我們的第一列;

      嘗試第五行及以后幾行,黑色棋子對我們并無影響。

      右斜線則相反:

      隨著行序號增加,影響的列序號也增加,直到影響的列序號大于8就不再影響。

      我們對于之前棋子影響的區(qū)域,可以用二進(jìn)制數(shù)字來表示,比如:

      每一位,用01代表是否影響。

      比如上圖,對于第一行,就是00010000

      嘗試第二行時,數(shù)字變?yōu)?0100000

      第三行:01000000

      第四行:10000000

      對于右斜線的數(shù)字,同理:

      第一行00010000,之后向右移:00001000,00000100,00000010,00000001,直到全0不影響。

      同理,我們對于多行數(shù)據(jù),也同樣可以記錄了

      比如在第一行我們放在了第四列:

      第二行放在了G列,這時左斜線記錄為00100000(第一個棋子的影響)+00000010(當(dāng)前棋子的影響)=00100010。

      到第三行數(shù)字繼續(xù)左移:01000100,然后繼續(xù)加上我們的選擇,如此反復(fù)。

      這樣,我們對于當(dāng)前位置的判斷,其實(shí)可以通過左斜線變量、右斜線變量、列變量,按位或運(yùn)算求出(每一位中,三個數(shù)有一個是1就不能再放)。

      具體看代碼:

      注:怎么排版就炸了呢。。。貼一張圖吧

      public class NQueens {

      public static int num2(int n) {

      // 因?yàn)楸痉椒ㄖ形贿\(yùn)算的載體是int型變量,所以該方法只能算1~32皇后問題

      // 如果想計(jì)算更多的皇后問題,需使用包含更多位的變量

      if (n < 1 || n > 32) {

      return 0;

      }

      int upperLim = n == 32 ? -1 : (1 << n) - 1;

      //upperLim的作用為棋盤大小,比如8皇后為00000000 00000000 00000000 11111111

      //32皇后為11111111 11111111 11111111 11111111

      return process2(upperLim, 0, 0, 0);

      }

      public static int process2(int upperLim, int colLim, int leftDiaLim,

      int rightDiaLim) {

      if (colLim == upperLim) {

      return 1;

      }

      int pos = 0; //pos:所有的合法位置

      int mostRightOne = 0; //所有合法位置的最右位置

      //所有記錄按位或之后取反,并與全1按位與,得出所有合法位置

      pos = upperLim & (~(colLim | leftDiaLim | rightDiaLim));

      int res = 0;//計(jì)數(shù)

      while (pos != 0) {

      mostRightOne = pos & (~pos + 1);//取最右的合法位置

      pos = pos - mostRightOne; //去掉本位置并嘗試

      res += process2(

      upperLim, //全局

      colLim | mostRightOne, //列記錄

      //之前列+本位置

      (leftDiaLim | mostRightOne) << 1, //左斜線記錄

      //(左斜線變量+本位置)左移

      (rightDiaLim | mostRightOne) >>> 1); //右斜線記錄

      //(右斜線變量+本位置)右移(高位補(bǔ)零)

      }

      return res;

      }

      public static void main(String[] args) {

      int n = 8;

      System.out.println(num2(n));

      }

      }

      完整測試代碼:

      32皇后:結(jié)果/時間

      暴力搜:時間就太長了,懶得測。。。

      public class NQueens {

      public static int num1(int n) {

      if (n < 1) {

      return 0;

      }

      int[] record = new int[n];

      return process1(0, record, n);

      }

      public static int process1(int i, int[] record, int n) {

      if (i == n) {

      return 1;

      }

      int res = 0;

      for (int j = 0; j < n; j++) {

      if (isValid(record, i, j)) {

      record[i] = j;

      res += process1(i + 1, record, n);

      }

      }

      return res;

      }

      public static boolean isValid(int[] record, int i, int j) {

      for (int k = 0; k < i; k++) {

      if (j == record[k] || Math.abs(record[k] - j) == Math.abs(i - k)) {

      return false;

      }

      }

      return true;

      }

      public static int num2(int n) {

      if (n < 1 || n > 32) {

      return 0;

      }

      int upperLim = n == 32 ? -1 : (1 << n) - 1;

      return process2(upperLim, 0, 0, 0);

      }

      public static int process2(int upperLim, int colLim, int leftDiaLim,

      int rightDiaLim) {

      if (colLim == upperLim) {

      return 1;

      }

      int pos = 0;

      int mostRightOne = 0;

      pos = upperLim & (~(colLim | leftDiaLim | rightDiaLim));

      int res = 0;

      while (pos != 0) {

      mostRightOne = pos & (~pos + 1);

      pos = pos - mostRightOne;

      res += process2(upperLim, colLim | mostRightOne,

      (leftDiaLim | mostRightOne) << 1,

      (rightDiaLim | mostRightOne) >>> 1);

      }

      return res;

      }

      public static void main(String[] args) {

      int n = 32;

      long start = System.currentTimeMillis();

      System.out.println(num2(n));

      long end = System.currentTimeMillis();

      System.out.println("cost time: " + (end - start) + "ms");

      start = System.currentTimeMillis();

      System.out.println(num1(n));

      end = System.currentTimeMillis();

      System.out.println("cost time: " + (end - start) + "ms");

      }

      }

      數(shù)據(jù)結(jié)構(gòu)

      版權(quán)聲明:本文內(nèi)容由網(wǎng)絡(luò)用戶投稿,版權(quán)歸原作者所有,本站不擁有其著作權(quán),亦不承擔(dān)相應(yīng)法律責(zé)任。如果您發(fā)現(xiàn)本站中有涉嫌抄襲或描述失實(shí)的內(nèi)容,請聯(lián)系我們jiasou666@gmail.com 處理,核實(shí)后本網(wǎng)站將在24小時內(nèi)刪除侵權(quán)內(nèi)容。

      版權(quán)聲明:本文內(nèi)容由網(wǎng)絡(luò)用戶投稿,版權(quán)歸原作者所有,本站不擁有其著作權(quán),亦不承擔(dān)相應(yīng)法律責(zé)任。如果您發(fā)現(xiàn)本站中有涉嫌抄襲或描述失實(shí)的內(nèi)容,請聯(lián)系我們jiasou666@gmail.com 處理,核實(shí)后本網(wǎng)站將在24小時內(nèi)刪除侵權(quán)內(nèi)容。

      上一篇:怎么刪除word檔的空白頁(在word里面怎么刪掉空白頁)
      下一篇:【我理想的云會議】我對目前云會議使用的感受
      相關(guān)文章
      亚洲国产成人精品久久久国产成人一区二区三区综 | 亚洲av日韩综合一区在线观看| 亚洲专区在线视频| 国产日本亚洲一区二区三区 | 亚洲日韩图片专区第1页| 亚洲av乱码中文一区二区三区| 亚洲国产a∨无码中文777| 亚洲精品无码久久久久牙蜜区| 亚洲av无码不卡| 亚洲人成网站18禁止一区| 亚洲一级毛片中文字幕| 亚洲国产精品成人久久| 亚洲精品高清无码视频| 激情婷婷成人亚洲综合| 色老板亚洲视频免在线观| 亚洲人配人种jizz| 亚洲色精品VR一区区三区| 亚洲成人在线电影| 亚洲邪恶天堂影院在线观看| 亚洲一区二区三区四区在线观看| 亚洲欧洲日产国码久在线观看| 亚洲AV无码精品无码麻豆| 久久久亚洲精品视频| 亚洲视频.com| 亚洲成综合人影院在院播放| 在线观看亚洲av每日更新| 久久久久亚洲国产AV麻豆 | 亚洲中文字幕一区精品自拍| 亚洲中文字幕无码av| 亚洲av色香蕉一区二区三区蜜桃| 精品久久亚洲一级α| 亚洲精品麻豆av| 国产精品观看在线亚洲人成网| 亚洲av永久无码嘿嘿嘿| 中文字幕亚洲男人的天堂网络| 亚洲熟妇无码AV| 亚洲日韩国产精品乱| 国产亚洲AV无码AV男人的天堂 | 中文无码亚洲精品字幕| 国产成人精品日本亚洲语音| 亚洲精品tv久久久久|