1. 程式人生 > 實用技巧 >QTableView載入百萬條資料佔用記憶體少

QTableView載入百萬條資料佔用記憶體少

問題概述:

八皇后問題,是指在8x8的國際象棋棋盤上放置8個皇后,保證任意兩個皇后都無法互相攻擊的問題。皇后的攻擊範圍為:上下左右斜對角線都可以攻擊到。示意圖如下:現在,有一個這樣的問題:現在已經在棋盤上擺放了k個皇后,且這k個皇后的格子位置已經給出。請編寫一個程式,根據給出的k個有皇后的格子,輸出已擺放8個皇后的國際象棋棋盤。(請注意:這題有唯一解!)



輸入例項:

2

2 2

5 3


輸出例項:

......Q.

Q.......

..Q.....

.......Q

.....Q..

...Q....

.Q......

....Q...


解題思路:

這道題是回溯法的經典問題,一般的思路是:在第一行的任意位置擺放第一個皇后,接著在第二行且不會被其他皇后攻擊的任意位置擺放第二個皇后..... 直到皇后擺放完畢。因此,我們需要使用遞迴的方式來依次完成八個皇后的擺放。並且使用4個數組來存放行、列、左對角線、右對角線是否處於皇后的攻擊範圍內。依次如下:(注意:在這裡由於是8x8的棋盤,所以這裡N為8)

  • row[N] 如果row[N]不為空,則x行受到攻擊
  • col[N] 如果col[N]不為空,則x列受到攻擊
  • dpos[2N-1] 如果dpos[2N-1]不為空,則斜向左下的x列受到攻擊
  • dneg[2N-1] 如果dneg[2N-1]不為空,則斜向右下的x列受到攻擊

既然這些陣列有了,那麼我們怎樣在擺放皇后的時候,去設定皇后的攻擊範圍呢?這裡給出推導的一個公式:

  • row[i] i 的範圍為 0-7 代表皇后攻擊的範圍為第i行
  • col[j] j 的範圍為 0-7 代表皇后攻擊的範圍為第j列
  • dpos[i+j] i+j 的範圍為 0-14 代表皇后攻擊的範圍為斜向左下的i+j列
  • dneg[(i-j)+(N-1)] (i-j)+(N-1)的範圍為 0-14 代表皇后攻擊的範圍為斜向右下的(i-j)+(N-1)列

只要row[i],col[j],dpos[i+j],dneg[(i-j)+(N-1)]中有任意一個不為空,格子(i,j)就會收到攻擊。也就是說,當row[i],col[j],dpos[i+j],dneg[(i-j)+(N-1)]全部都為空時,皇后才能放置在(i,j)格子中。


其中dpos和dneg陣列的範圍為0-14,共有15條斜線。(如果你要疑問為什麼為15條,可以自行去畫一下圖,列出所有的左斜線和右斜線。數個數去看一下。)


但是,這道題有一個限定條件就是有些皇后事先已經擺放好了,所以如果我們要是直接進行遞迴的話,是無法進行遞迴的。(例如:有個皇后的位置為:(2,2)。那麼,當你一行一行的進行遞迴時,由於第二行已經有了皇后。那麼,第二行就無法擺放棋子。所以,就導致了無法遞迴的現象出現。


所以,我們應該如何避免這個問題呢?在這裡給出思路:我們首先將已經擺放好的皇后做出標記。在之後,可以將所有八個皇后的擺放方式遞迴出來,然後根據已經擺放好的皇后標記,從所有八個皇后的擺放方式中篩選帶有事先擺放好的皇后的情況。最後輸出即可。(可能這段話讓人難以理解。(難以理解的話,就閱讀一下程式碼吧!))


程式碼如下:

#include <iostream>
#include <cstdio>
using namespace std;
bool Graph[8][8];           //定義一個8x8的棋盤(圖)
int row[8];                 //代表是否受到攻擊的行(初始化均為0,代表沒有受到攻擊,為1代表受到攻擊)
int col[8];                 //代表是否受到攻擊的列
int dpos[15];               //代表是否受到攻擊的左對角線
int dneg[15];               //代表是否受到攻擊的右對角線
void dfs(int deep);         //代表深度優先搜尋(回溯)的函式,deep代表搜尋的深度
int k, h, w;                //代表皇后的個數,以及皇后的所在的行列座標

void dfs(int deep)           //通過dfs,來檢索出所有擺放八皇后的情況
{
	int i, j;
 	if (deep == 8)          //代表八個皇后全部放置完成
	{
		for (i = 0; i < 8; i++)
		{
			for (j = 0; j < 8; j++)
			{
				if (Graph[i][j])                    //如果當前位置有事先設定好的皇后的話
				{
					if (row[i] != j)               //如果當前的擺放情況不滿足事先給定好的情況的話。(由於,這道題有部分皇后是提前擺好的,所以我們需要搜尋出所有擺放好的八皇后的情況,從中篩選出提前擺好的情況)
					{
						return;                    //不滿足情況的話,直接進行退出
					}
				}
			}
		}
		for (i = 0; i < 8; i++)                    //如果擺放情況滿足於事先給定好的情況的話就輸出棋盤。
		{
			for (j = 0; j < 8; j++)
			{
					if (row[i] == j)
					{
						printf("Q");
					}
					else
					{
						printf(".");
					}
			}
			cout << endl;
		}
		return;
	}
	for (i = 0; i < 8; i++)                        //搜尋當前行中的列
	{
		if (row[deep] == 0 && col[i] == 0 && dpos[deep+i] == 0 && dneg[(deep-i)+(8-1)] == 0)         //如果當前行中的列不是處於皇后的行、列、左對角線、右對角線的攻擊範圍內
		{
			row[deep] = i;                         //放置皇后
			col[i] = 1;
			dpos[deep + i] = 1;
			dneg[(deep - i) + (8 - 1)] = 1;
			dfs(deep + 1);                         //在下一行中繼續搜尋
			row[deep] = 0;                         //把原先放置皇后的所有操作進行還原(回溯操作)
			col[i] = 0;
			dpos[deep + i] = 0;
			dneg[(deep - i) + (8 - 1)] = 0;
		}
	}
	return;                                      //代表如果當前行中無法放置皇后的話,那麼就回溯到上一層重新找列放置皇后
}

int main() 
{
	int i,j;
	for (i = 0; i < 8; i++)
	{
		for (j = 0; j < 8; j++)
		{
			Graph[i][j] = false;                //代表初始化棋盤
		}
	}
	scanf("%d", &k);
	for (i = 0; i < k; i++)
	{
		scanf("%d %d", &h, &w);
		Graph[h][w] = true;                  //放置事先設定好的皇后
	}
	dfs(0);                                  //代表進行搜尋
}