排列組合相關問題(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);
}
}
小思:
遇到抽象問題,我們要學會怎麼具體化。畫圖、舉例子和分解這三種方法不失為一種好的決策。
圖形能使抽象的問題形象化。當面試題設計連結串列、二叉樹等資料結構時,如果在紙上畫幾張草圖,題目中隱藏的規律就很可能變得很直觀。
一兩個例子能使抽象的問題具體化,很多與演算法相關的問題都很抽象,未必一眼就能看出它們的規律。這個時候我們不妨舉幾個例子,一步一步模擬執行過程,說不定就能發現其中的規律,從而找到解決問題的竅門。
把複雜問題分解成若干個小問題,是解決很多複雜問題的有效辦法。如果遇到問題很大,可以嘗試先把大問題分解成小問題,然後再遞迴地解決這些小問題。分治法、動態規劃等方法都是應用分解複雜問題的思路。