题目:
输入一个含有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);
}
}
小思:
遇到抽象问题,我们要学会怎么具体化。画图、举例子和分解这三种方法不失为一种好的决策。
图形能使抽象的问题形象化。当面试题设计链表、二叉树等数据结构时,如果在纸上画几张草图,题目中隐藏的规律就很可能变得很直观。
一两个例子能使抽象的问题具体化,很多与算法相关的问题都很抽象,未必一眼就能看出它们的规律。这个时候我们不妨举几个例子,一步一步模拟运行过程,说不定就能发现其中的规律,从而找到解决问题的窍门。
把复杂问题分解成若干个小问题,是解决很多复杂问题的有效办法。如果遇到问题很大,可以尝试先把大问题分解成小问题,然后再递归地解决这些小问题。分治法、动态规划等方法都是应用分解复杂问题的思路。