皇后問題

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

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

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

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

?

一般思路:

?

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

?

優化1:

?

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

我們生成一維數組record,record[i]表示第i行的皇后放在了第幾列。對于每一行,確定當前record值即可,因為每行只能且必須放一個皇后,放了一個就無需繼續嘗試。那么對于當前的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);}}//對于當前行,依次嘗試每列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 void main(String[] args) {int n = 8;System.out.println(num1(n));}
}

優化2:

?

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

黑色棋子影響區域為紅色

本行影響不提,根據優化一已經避免

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

?

左斜線:每向下一行,實際上對當前行的影響區域就向左移動

比如:

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

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

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

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

?

右斜線則相反:

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

?

我們對于之前棋子影響的區域,可以用二進制數字來表示,比如:

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

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

嘗試第二行時,數字變為00100000

第三行:01000000

第四行:10000000

?

對于右斜線的數字,同理:

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

?

同理,我們對于多行數據,也同樣可以記錄了

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

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

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

?

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

具體看代碼:

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

public class NQueens {public static int num2(int n) {// 因為本方法中位運算的載體是int型變量,所以該方法只能算1~32皇后問題// 如果想計算更多的皇后問題,需使用包含更多位的變量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 11111111return 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;//計數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 = 8;System.out.println(num2(n));}
}

完整測試代碼:

32皇后:結果/時間

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

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");}
}

?

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/445470.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/445470.shtml
英文地址,請注明出處:http://en.pswp.cn/news/445470.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

c++基礎學習(04)--(函數、數字、數組、字符串)

文章目錄目錄1.函數2.數字3.字符串4.數組目錄 1.函數 #include <iostream> #include <limits>using namespace std;void swap(int *x , int *y);int main(){int a 100 , b200;cout<<"交換前:"<<"a is :"<<a<<"…

【精品計劃0】藍橋杯 摔手機

原題描述&#xff1a; x星球的居民脾氣不太好&#xff0c;但好在他們生氣的時候唯一的異常舉動是&#xff1a;摔手機。 各大廠商也就紛紛推出各種耐摔型手機。x星球的質監局規定了手機必須經過耐摔測試&#xff0c;并且評定出一個耐摔指數來&#xff0c;之后才允許上市流通。 …

數據結構課上筆記15

圖的存儲 多重鏈表&#xff1a;完全模擬圖的樣子&#xff0c;每個節點內的指針都指向該指向的節點。 節點結構內指針數為度 缺點&#xff1a;浪費空間、不容易操作 數組表示法&#xff08;鄰接矩陣表示法&#xff09; 可用兩個數組存儲。其中一個 一維數組存儲數據元素&#…

c++基礎學習(05)--(指針,引用)

文章目錄目錄1.指針2.引用目錄 1.指針 #include <iostream>using namespace std;int main () {int var1;char var2[10];cout << "var1 變量的地址&#xff1a; ";cout << &var1 << endl;cout << "var2 變量的地址&#xff…

由旅行商問題認識何為狀態壓縮

動態規劃 動態規劃(dynamic programming)是運籌學的一個分支&#xff0c;是求解決策過程(decision process)最優化的數學方法。20世紀50年代初美國數學家R.E.Bellman等人在研究多階段決策過程(multistep decision process)的優化問題時&#xff0c;提出了著名的最優化原理(pri…

c++基礎學習(06)--(時間,輸入輸出,數據結構)

文章目錄目錄1.時間2.輸入輸出數據結構目錄 1.時間 當前日期和時間 下面的實例獲取當前系統的日期和時間&#xff0c;包括本地時間和協調世界時&#xff08;UTC&#xff09;。 #include <iostream> #include <ctime>using namespace std;int main( ) {// 基于當前…

Abstract Self-Balancing Binary Search Tree

二叉搜索樹 二叉查找樹&#xff08;Binary Search Tree&#xff09;&#xff0c;&#xff08;又&#xff1a;二叉搜索樹&#xff0c;二叉排序樹&#xff09;它或者是一棵空樹&#xff0c;或者是具有下列性質的二叉樹&#xff1a; 若它的左子樹不空&#xff0c;則左子樹上所有結…

AVL Tree

前言 希望讀者 了解二叉搜索樹 了解左旋右旋基本操作 https://blog.csdn.net/hebtu666/article/details/84992363 直觀感受直接到文章底部&#xff0c;有正確的調整策略動畫&#xff0c;自行操作。 二叉搜索樹 二叉查找樹&#xff08;Binary Search Tree&#xff09;&a…

c++基礎學習(07)--(類)

文章目錄目錄類與對象1.類成員函數2.類訪問修飾符3.構造函數與析構函數4.拷貝構造函數5. 友元函數6.內聯函數7.this指針8.指向類的指針9.類的靜態成員目錄 類與對象 #include <iostream>using namespace std;class Box {public:double length; // 長度double breadth;…

【大總結1】數據結構與傳統算法總結

由于時間和水平有限&#xff0c;肯定有錯誤或者寫得不好的地方 歡迎在文章下評論指出。 涉及語言&#xff1a; py3&#xff1a;注重算法本身的知識 c/c&#xff1a;實現基礎數據結構和算法 java&#xff1a;實現較復雜數據結構 一、概述 c語言知識體系 算法體系參考 課上筆…

c++基礎學習(08)--(繼承、重載、多態、虛函數)

文章目錄目錄1.繼承2.重載3.多態 && 虛函數目錄 1.繼承 #include <iostream>using namespace std;// 基類 class Shape {public:void setWidth(int w){width w;}void setHeight(int h){height h;}protected:int width;int height; };// 派生類 class Rectang…

圖的應用

1. 圖的應用總覽 在數據結構中圖的應用很廣泛&#xff0c;本文主要從以下四個方面介紹&#xff1a; ①最小生成樹&#xff1a;給定一個無向網絡&#xff0c;在該網的所有生成樹中&#xff0c;使得各邊權數之和最小的那棵生成樹稱為該網的最小生成樹&#xff0c;也叫最小代價…

c++基礎學習(09)--(數據抽象、數據封裝、接口)

文章目錄目錄1.數據抽象2.數據封裝3.抽象接口類目錄 1.數據抽象 數據抽象&#xff1a;就是把它當做黑箱子使用&#xff0c;內部實現與外部接口分開 C類實現數據抽象&#xff0c;如sort()函數&#xff0c;ostream的cout對象 #include <iostream> using namespace std…

吃豆人游戲開發

這個文章怎么突然這么多人看啊。請大家多關注我其他文章啊 html&#xff1a; <html> <head> <meta charset"utf8"> <title>html5 pacman吃豆人游戲代碼</title><style>body{background-color: #292929}*{padding:0;margin:0;}.w…

c++基礎學習(10)--(文件、流、異常處理、動態內存、命名空間)

文章目錄目錄1.文件和流2.異常處理3.動態內存4.命名空間目錄 1.文件和流 注意 文件打開方式中的in和out都是相對于內存&#xff08;計算機&#xff09;而言的&#xff0c;計算機讀取文件&#xff0c;是將數據從磁盤中的文件讀入到內存中&#xff0c;所以用的是in #include &…

數據結構和算法(02)---字符串(c++)

文章目錄目錄一.c風格的字符串與操作函數1.c風格字符串2.c風格字符串處理函數二.c中的字符串與操作函數1.c中的string類2.string類的基本操作3.string類的操作匯總目錄 數據結構&#xff1a; 邏輯結構&#xff1a;數組&#xff0c;棧&#xff0c;隊列&#xff0c;字符串&#x…

如何學習數據結構和算法——大佬文章匯總

第一篇 第二篇、 作者&#xff1a;左程云 我分別說一下國內和國外的行情。 國內的話&#xff0c;一般來講&#xff0c;工資高的公司在面試時算法和數據結構題目的比重較大&#xff0c;工資一般的公司比重較小。當然同樣公司的不同崗位&#xff0c;要求也會不同&#xff0c;…

c++基礎學習(13)--(STL、標準庫)

文章目錄目錄1. STL教程2.標準庫3.有用的資源目錄 1. STL教程 #include <iostream> #include <vector> using namespace std;int main() {// 創建一個向量存儲 intvector<int> vec; int i;// 顯示 vec 的原始大小cout << "vector size " &…

哈夫曼實現文件壓縮解壓縮(c語言)

寫一個對文件進行壓縮和解壓縮的程序&#xff0c;功能如下&#xff1a; ① 可以對純英文文檔實現壓縮和解壓&#xff1b; ② 較好的界面程序運行的說明。 介紹哈夫曼&#xff1a; 效率最高的判別樹即為哈夫曼樹 在計算機數據處理中&#xff0c;霍夫曼編碼使用變長編碼表對源…

c++基礎學習(11)--(模板、預處理器、信號處理)

文章目錄目錄1.模板2.預處理器3.信號處理目錄 1.模板 模板是泛型編程的基礎&#xff0c;泛型編程&#xff1a;以一種獨立于任何特定類型的方式 #include <iostream> #include <string>using namespace std;template <typename T> inline T const& Max…