排列组合相关问题(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);
	}
}

小思:

遇到抽象问题,我们要学会怎么具体化。画图、举例子和分解这三种方法不失为一种好的决策。

图形能使抽象的问题形象化。当面试题设计链表、二叉树等数据结构时,如果在纸上画几张草图,题目中隐藏的规律就很可能变得很直观。

一两个例子能使抽象的问题具体化,很多与算法相关的问题都很抽象,未必一眼就能看出它们的规律。这个时候我们不妨举几个例子,一步一步模拟运行过程,说不定就能发现其中的规律,从而找到解决问题的窍门。

把复杂问题分解成若干个小问题,是解决很多复杂问题的有效办法。如果遇到问题很大,可以尝试先把大问题分解成小问题,然后再递归地解决这些小问题。分治法、动态规划等方法都是应用分解复杂问题的思路。

猜你喜欢

转载自blog.csdn.net/u013132035/article/details/80649276
今日推荐