1. 程式人生 > >排列組合相關問題(Java)

排列組合相關問題(Java)

題目:

輸入一個含有8個數字的陣列,判斷有沒有可能把這8個數字分別放到正方體的8個頂點上(如下圖所示),使得正方體上三組相對的面上的4個頂點的和都相等。


思路:

相當於先得到a1、a2、a3、a4、a5、a6、a7和a8這8個數字的所有排列,然後判斷有沒有某一個的排列複合題目所給定的條件,即a1 + a2 + a3 + a4 == a5 + a6 + a7 + a8,a1 + a3 + a5 + a7 == a2 + a4 + a6 + a8,並且a1 + a2 + a5 + a6 == a3 + a4 + a7 + a8。

程式碼實現:

public static void permutation(int[] nums) {
	if (nums == null || nums.length != 8) {
		return;
	}
	boolean flag = permutation(nums, 0, nums.length - 1);
	if (flag) {
		System.out.println("ok");
	} else {
		System.out.println("sorry");
	}
}
private static boolean permutation(int[] nums, int start, int end) {
	if (nums == null || nums.length != 8) {
		return false;
	}
	if (start == end) {
		return false;
	}
	if (isEqual(nums)) {
		return true;
	} else {
		for (int i = start; i <= end; i++) {
			int temp = nums[start];//交換位置
			nums[start] = nums[i];
			nums[i] = temp;

			permutation(nums, start + 1, end);
			temp = nums[start];//復原
			nums[start] = nums[i];
			nums[i] = temp;
		}
	}
	return false;
}
// 判斷是否存在這樣一個全排列陣列使其滿足
private static boolean isEqual(int[] nums) {
	if (nums == null) {
		return false;
	}
	/*
	 * a1 + a2 + a3 + a4 == a5 + a6 + a7 + a8 a1 + a3 + a5 + a7 == a2 + a4 +
	 * a6 + a8 a1 + a2 + a5 + a6 == a3 + a4 + a7 + a8
	 */
	int result1 = nums[0] + nums[1] + nums[2] + nums[3];
	int result2 = nums[4] + nums[5] + nums[6] + nums[7];
	int result3 = nums[0] + nums[2] + nums[4] + nums[6];
	int result4 = nums[1] + nums[3] + nums[5] + nums[7];
	int result5 = nums[0] + nums[1] + nums[4] + nums[5];
	int result6 = nums[2] + nums[3] + nums[6] + nums[7];
	if (result1 == result2 && result3 == result4 && result5 == result6) {
		return true;
	}
	return false;
}

題目:

在8*8的國際象棋上拜訪8個皇后,使其不能相互攻擊,即任意兩個皇后不得處在同一行、同一列或者同一對角線上。下圖中的每個帶有皇冠的格子表示一個皇后,這就是一種符合條件的擺放方法。請問總共有多少種符合條件的擺法?

  

思路:

由於8個皇后的任意兩個不能處在同一行,那麼肯定是每一個皇后佔據一行。於是我們可以定義一個數組ColumnIndex[8],陣列中第i個數字表示位於第i行的皇后的列號。先把陣列ColumnIndex的8個數字分別用0~7初始化,接下來就是對陣列ColumnIndex做全排列。因為我們是用不同的數字初始化陣列,所以任意兩個皇后肯定不同列。我們只需判斷每一個排列對應的8個皇后是不是在同一對角線上,也就是對於陣列的兩個下標i和j,是不是i - j == ColumnIndex[i] - ColumnIndex[j] 或者 j - i == ColumnIndex[i] - ColumnIndex[j]。

//八皇后問題
public class Solution {

	/**
	 * 一共有多少個皇后(此時設定為8皇后在8X8棋盤,可以修改此值來設定N皇后問題)
	 */
	int max = 8;
	/**
	 * 該陣列儲存結果,第一個皇后擺在array[0]列,第二個擺在array[1]列
	 */
	int[] array = new int[max];

	/**
	 * 記錄擺法數量
	 */
	private static int count = 0;
	
	/**
	 * n代表當前是第幾個皇后
	 * @param n
	 * 皇后n在array[n]列
	 */
	private void check(int n) {
		// 終止條件是最後一行已經擺完,由於每擺一步都會校驗是否有衝突,所以只要最後一行擺完,說明已經得到了一個正確解
		if (n == max) {
			print();
			++count;
			return;
		}
		// 從第一列開始放值,然後判斷是否和本行本列本斜線有衝突,如果OK,就進入下一行的邏輯
		for (int i = 0; i < max; i++) {
			array[n] = i;
			if (judge(n)) {
				check(n + 1);
			}
		}
	}

	//判斷是否在同一行,同一對角線上,是的話,不滿足條件
	private boolean judge(int n) {
		for (int i = 0; i < n; i++) {
			if (array[i] == array[n] || Math.abs(n - i) == Math.abs(array[n] - array[i])) {
				return false;
			}
		}
		return true;
	}
	
	//列印皇后擺法數量
	private void print() {
		for (int i = 0; i < array.length; i++) {
			System.out.print(array[i] + 1 + " ");
		}
		System.out.println();
	}

	public static void main(String[] args) {
		Solution s1 = new Solution();
		s1.check(0);
		System.out.println(count);
	}
}

小思:

遇到抽象問題,我們要學會怎麼具體化。畫圖、舉例子和分解這三種方法不失為一種好的決策。

圖形能使抽象的問題形象化。當面試題設計連結串列、二叉樹等資料結構時,如果在紙上畫幾張草圖,題目中隱藏的規律就很可能變得很直觀。

一兩個例子能使抽象的問題具體化,很多與演算法相關的問題都很抽象,未必一眼就能看出它們的規律。這個時候我們不妨舉幾個例子,一步一步模擬執行過程,說不定就能發現其中的規律,從而找到解決問題的竅門。

把複雜問題分解成若干個小問題,是解決很多複雜問題的有效辦法。如果遇到問題很大,可以嘗試先把大問題分解成小問題,然後再遞迴地解決這些小問題。分治法、動態規劃等方法都是應用分解複雜問題的思路。