N皇后問題(遞歸回溯)

  今天講了N後問題,現在來複習一下。

  N後問題就是在N*N格的棋盤上面放置彼此不受攻擊的n個皇后。這裏的皇后指的是國際象棋中的皇后,按照國際象棋的規則,皇后可以攻擊當前行和當前列還有同一斜線的棋子。簡單來說,就是n個皇后的位置不可以在同一行,同一列,同一斜線。因爲這幾天學習的是回溯算法,很簡單的想到了回溯。這個問題也是經典的回溯算法習題之一。

  下面來想一下問題所包含的條件,明顯的條件就是棋盤是n*n,而且很簡單就想到n個皇后不可以在同一列,就是每一列都會有一個皇后。下面就是隱式的條件了,同一行和同一列都可以很簡單的解決,同一斜線不是很好想。後來發現同一斜線的皇后都是皇后的位置差和皇后所在的列相等。就是皇后的位置斜率爲1。翻譯爲代碼語言就是,皇后所在的位置的差的絕對值等於皇后的列的差的絕對值。這個後面還會講一下。條件都講完了,下面就說一說算法的主體。

  算法的主體是由遞歸構成,遞歸的參數就是當前考慮的皇后的列,爲了下面的計算方便,這裏將皇后的個數和數組都傳了進去。爲什麼傳進去數組?因爲自己一開始想的是一個二維數組,這個也是大家容易想到的,但是後來發現,解的形式和數組的下標有關,乾脆將二維數組變爲了一維數組,將皇后所在的列變爲數組的下標。這樣,就省去了很多的無用功。遞歸的邊界就是k的位置大於等於n。遞歸的主體是一個for循環,將循環的值賦值給數組當前的位置,同時檢驗當前位置是否正確,如果正確就進行下一個遞歸(k的值加一),不正確就進行下一次循環。講到了檢驗k的位置,就說一說這個方法,很簡單,就是判斷當前位置是不是符合問題的條件,要有一個返回值,方便後面的調用。檢驗的時候也是要用到for循環。

  算法的主體講完了,下面就直接粘貼代碼,代碼如下:

package sf;

import java.util.Scanner;

//N後問題
public class demo7 
{
	public static int num=0;//累加和
	//判斷當前位置是否正確
	public static Boolean judge(int[] p,int k)
	{
		for(int i=0;i<k;i++)
		{
			//絕對值的判定和列的判定。(不用判定行)
			if((Math.abs(i-k)==Math.abs(p[i]-p[k]))||(p[i]==p[k])) {
				return false;
			}	
		}
	  //要注意這個return的位置,在這裏是爲了讓for循環能夠一直循環下去,不被中間的位置打斷,可以將
	  //這個return放到for循環中體驗一下。會發現,解的個數會變多。
	  return true;
	}
	//算法主體  遞歸回溯 k爲當前考慮位置
	public static void Queen(int[] p,int n,int k)
	{
		//這裏的方法主體還是要多想一想
		if(k>=n)
		{
			num++;
			//打印結果
			for(int i=0;i<n;i++)
				System.out.print(p[i]);
			System.out.println("");
		}else{
			for(int j=1;j<=n;j++)
			{
				//System.out.println(k);
				//將皇后的位置賦值給數組中的元素,相當於放置皇后
				p[k]=j;
				if(judge(p, k))
				{
					//遞歸下一個位置
					Queen(p, n, k+1);
				}
			}
		}
	}
	public static void main(String[] args)
	{
		System.out.println("請輸入皇后的個數:");
		Scanner sc=new Scanner(System.in);
		int n=sc.nextInt();
		int []p=new int[n];
		Queen(p,n,0);
		//將結果打印出來,如果只是打印這一個計數,程序的運行會快一點。
		System.out.println(n+"皇后問題的解共:"+num+"種");
	}

}

  算法的代碼就是這樣,算法的主體還是不好想的,我想了一會,感覺還是存在着一些問題,但是代碼執行的結果正確,就不在想了。要注意算法中的for循環的指針的大小,這裏還是要想一想的,雖然只有簡單的一維數組。下面就講一講自己的一些想法,這個算法就是簡單的遞歸求解,說的明白一點,就是窮舉法,這就是這個算法的不足之處。只是在簡單的列舉之後,並沒有進行函數限制。這個和回溯的思想有些不一樣,回溯的思想是算法的主體還要有函數限制,將後面的解的一些錯誤的子樹直接去掉,這個算法沒有做到這一步,算法的實現還是有很大的提升空間,大概在求16皇后問題的時候需要100秒就是算法的大概極限了(這個也是在網上看到的),自己還是一個菜鳥,現在就是想一想。還是理解回溯的思想,在窮舉的時候對於結果進行函數限制,方便後面的列舉,大大加快算法的效率。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章