1. 程式人生 > >用回溯法解決八皇后問題的思路,並求出17皇后解的數量(c#,c++,python表示)

用回溯法解決八皇后問題的思路,並求出17皇后解的數量(c#,c++,python表示)

1.解決思路

借用網上一張圖,中間那個紅點表示的就是皇后,這圖的意思也就是一個皇后的影響範圍為這這個皇后所在的那一列,那一行,對角線以及次對角線。其它的皇后不能在它的影響範圍裡否則會衝突。八皇后也就是在一個8x8的棋盤上後置8個皇后,皇后的數量與棋盤的行列數一樣。這是基礎,再來說說回溯法,回溯法最重要的思想就是當前這一步走不通,我們就掉頭返回上一步找其他方案。這相對於列舉法列出所有可能再逐個進行排除要節省資源的多。

首先皇后的衝突規則是在同一行,同一列,同一對角線,同一次對角線。因為多個皇后不可能在同一行,所以我們可以看成第一個皇后在第一行,第二個皇后在第二行.........再用一個值來表示陣列來表示皇后是處於第幾列的,那麼某個皇后在棋盤上的位置就可以用這是第幾個皇后表示y,這個皇后對應數組裡的值表示x。並且這樣對於檢測衝突可以少對比一次,只需要檢測時候是否處於同一列,同一對角線,同一次對角線即可。

再來說說回溯法在n皇后問題上的應用,在棋盤上我們可以先放第一個皇后,再放一個皇后再檢驗衝突,如果遇到衝突將這個皇后的位置向右移動,如果這個皇后移動超出了棋盤,這表明這種情況下沒有解,對此可以把這個皇后拿出棋盤並移動上一個皇后,直到上一個皇后也不衝突,在繼續放置下一個皇后,找出不衝突的位置,如果到了就這樣放到了第八個皇后仍不衝突那麼得到一種解。在得到了一種解後再把第8個皇后拿出棋盤,移動第7個皇后,繼續找出不衝突的位置,如果第7個皇后剩下的位置全衝突,那把第7個皇后拿出棋盤,再移動第6個皇后的位置........就醬,如果第一個皇后移動超出了棋盤那麼表示已經找出所有解。

2.程式碼

using System;
namespace n皇后問題
{
    class Program
    {
        static int Queen_n =12;//皇后數量
        static int[] queens = new int[Queen_n];//儲存所有皇后的位置
        static int QueenSolution_n = 0;//有多少解
        static double time = 0;
        static void Main(string[] args)
        {
            time = Environment.TickCount;//記錄程式啟動時間
            for (int i = 0; i < Queen_n; i++)//對所有皇后初始化
            {
                queens[i] = 0;
            }
            nQueen(0);
            Console.WriteLine(Queen_n + "皇后問題有" + QueenSolution_n + "種解,用時"+(Environment.TickCount-time)+"ms");
            Console.ReadKey();
        }
        static bool IS = false;
        static void nQueen(int now)
        {
           
            for (int i = now; i < Queen_n; i++)
            {
                while (IsConlict(i) || queens[i] >= Queen_n)//如果當前皇后的位置與前幾個衝突,或者超出棋盤,進來返回上一層
                {
                    queens[i]++;//皇后位置右移
                    if (queens[i] >= Queen_n)//如果這皇后沒有結果
                    {
                        if (queens[0] >= Queen_n)//第一個皇后越界,結束
                        {
                            return;
                        }
                        queens[i] = 0;//當前皇后位置歸0
                        i--;//返回上一個皇后
                        queens[i]++;//當前皇后位置右移
                        nQueen(i);//
                        return ;//釋放
                    }
                }
             
                if (i == Queen_n - 1)//如果到了第八個皇后,且不衝突。得到一種解
                {
                    //Console.WriteLine("解+1,目前是第"+QueenSolution_n);
                    queens[i] = 0;//當前皇后位置歸0
                    i--;//返回上一個皇后
                    queens[i]++;//當前皇后位置右移
                    QueenSolution_n++;//解+1
                    nQueen(i);
                    return;
                }
            }
        }
        static bool IsConlict(int now)//當前是第now個皇后,一個皇后佔一行,所以它是第幾個皇后也就表示,這個皇后在第幾行
        {
            for (int i = 0; i < now; i++)
            {//在同一對角線,在同一斜對角線,在同一列上
                if (((i - now) == (queens[i] - queens[now])) || ((i - now) == -(queens[i] - queens[now])) || (queens[i] == queens[now]))
                {
                    return true;//衝突
                }
            }
            return false;
        }
    }
}

但是

這是由於遞迴太多導致的棧溢位,對此可以改變思路不是直接對函式遞迴而是迭代。

修改後的程式碼

using System;
namespace n皇后問題
{
    class Program
    {
        static int Queen_n =12;//皇后數量
        static int[] queens = new int[Queen_n];//儲存所有皇后的位置
        static int QueenSolution_n = 0;//有多少解
        static double time = 0;
        static void Main(string[] args)
        {
            time = Environment.TickCount;
            for (int i = 0; i < Queen_n; i++)//對所有皇后初始化
            {
                queens[i] = 0;
            }
            nQueen(0);
            Console.WriteLine(Queen_n + "皇后問題有" + QueenSolution_n + "種解,用時"+(Environment.TickCount-time)+"ms");
            Console.ReadKey();
        }
        static bool IS = false;
        static void nQueen(int now)
        {
           
            for (int i = now; i < Queen_n; i++)
            {
                while (IsConlict(i) || queens[i] >= Queen_n)//如果當前皇后的位置與前幾個衝突,或者超出棋盤,進來返回上一層
                {
                    queens[i]++;//皇后位置右移
                    if (queens[i] >= Queen_n)//如果這皇后沒有結果
                    {
                        if (queens[0] >= Queen_n)//第一個皇后越界,結束
                        {
                            return;
                        }
                        queens[i] = 0;//當前皇后位置歸0
                        i--;//返回上一個皇后
                        queens[i]++;//當前皇后位置右移
                        IS = true;
                    }
                }
                if (IS==true)//因為在for後會在++,所以如果是返回上一層需要再減一次
                {
                    i--;
                    IS = false;
                }
                if (i == Queen_n - 1)//如果到了第八個皇后,且不衝突。得到一種解
                {
                    queens[i] = 0;//當前皇后位置歸0
                    i--;//返回上一個皇后
                    queens[i]++;//當前皇后位置右移
                    QueenSolution_n++;//解+1
                    i--;
                }
            }
        }
        static bool IsConlict(int now)//當前是第now個皇后,一個皇后佔一行,所以它是第幾個皇后也就表示,這個皇后在第幾行
        {
            for (int i = 0; i < now; i++)
            {//在同一對角線,在同一斜對角線,在同一列上
                if (((i - now) == (queens[i] - queens[now])) || ((i - now) == -(queens[i] - queens[now])) || (queens[i] == queens[now]))
                {
                    return true;//衝突
                }
            }
            return false;
        }
    }
}

我這種弱雞畢竟不是ACM選手,也就只能這種程度了。

C++

// n皇后問題c++表示.cpp: 定義控制檯應用程式的入口點。
//

#include "stdafx.h"
#include <iostream>
#include <time.h>
using namespace std;

static int Queen_n = 12;//皇后數量
static int queens[20] = {0};//儲存所有皇后的位置
static int QueenSolution_n = 0;//有多少解

bool IsConlict(int now)//當前是第now個皇后,一個皇后佔一行,所以它是第幾個皇后也就表示,這個皇后在第幾行
{
	for (int i = 0; i < now; i++)
	{//在同一對角線,在同一斜對角線,在同一列上
		if (((i - now) == (queens[i] - queens[now])) || ((i - now) == -(queens[i] - queens[now])) || (queens[i] == queens[now]))
		{
			return true;//衝突
		}
	}
	return false;
}
void nQueen()
{

	for (int i = 0; i < Queen_n; i++)
	{
		while (IsConlict(i) || queens[i] >= Queen_n)//如果當前皇后的位置與前幾個衝突,或者超出棋盤,進來返回上一層
		{
			queens[i]++;//皇后位置右移
			if (queens[i] >= Queen_n)//如果這皇后沒有結果
			{
				if (queens[0] >= Queen_n)//第一個皇后越界,結束
				{
					return;
				}
				queens[i] = 0;//當前皇后位置歸0
				i--;//返回上一個皇后
				queens[i]++;//當前皇后位置右移
				i--;//因為在for後會在++,所以如果是返回上一層需要再減一次
			}
		}
		if (i == Queen_n - 1)//如果到了第八個皇后,且不衝突。得到一種解
		{
			queens[i] = 0;//當前皇后位置歸0
			i--;//返回上一個皇后
			queens[i]++;//當前皇后位置右移
			QueenSolution_n++;//解+1
			i--;
		}
	}
}

int main()
{
	double time = clock();
	nQueen();
	time =clock() - time;
	cout << Queen_n << "皇后問題有" << QueenSolution_n << "種解,用時" <<time<<"ms"<< endl;
	cin.get();
	return 0;
}

python

import time
Queen_n = 8#皇后數量
queens = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]#儲存所有皇后的位置
QueenSolution_n = 0#有多少解
def IsConlict(now):#當前是第now個皇后,一個皇后佔一行,所以它是第幾個皇后也就表示,這個皇后在第幾行
    for x in range(0,now):#在同一對角線,在同一斜對角線,在同一列上
        if (((x - now) == (queens[x] - queens[now])) or ((x - now) == -(queens[x] - queens[now])) or (queens[x] == queens[now])):
            return True#衝突
    return False  

def nQueens():
    i=0
    while i<Queen_n:
        while (IsConlict(i) or queens[i] >= Queen_n):
            queens[i]+=1
            if queens[i]>=Queen_n:
                if queens[0]>=Queen_n:#越界結束
                    return
                queens[i]=0
                i-=1
                queens[i]+=1
                i-=1
        if i==Queen_n-1:
            queens[i]=0
            i-=1
            queens[i]+=1
            global QueenSolution_n
            QueenSolution_n+=1
            i-=1
        i+=1    

time0=time.clock()
nQueens()
print(Queen_n, "皇后問題有",QueenSolution_n,"種解,用時" ,int((time.clock()-time0)*1000),"ms")