12个高矮不同的人,排成两排,每排必须是从矮到高排列,而且第二排比对应的第一排的人高,问排列方式有多少种?

12个高矮不同的人,排成两排,每排必须是从矮到高排列,而且第二排比对应的第一排的人高,问排列方式有多少种?

 

我们可以从如下几个方面来分析这个问题:

 

a. 先把这12个高矮不同的人按身高递增的顺序排成1列,并依次从1到12编号来简化不同身高的表示方法;

 

b. 如果第一排的排列次序已确定的话,那么第二排的排列就自然确定;

 

c. 显然,如果只有编号为1和2的2个人的话,排列方式只有一种(1)(2);

 

d. 对于编号为1 - n的每一种符合条件的排列(n为偶数,并且假设第一排末尾的编号为x),在第一排加入n+1和在第二排加入n+2可以产生一种符合条件的编号为1 - (n+2)的排列;

 

e. 在d条件下,可以把新加入第一排的n+1替换成 大于x且小于n+1 的编号(即第一排新末尾的编号值x'的取值范围为大于x且小于等于n+1),因为这样的替换保证了新的第一排的排列都是从原来的第一排生成的(即第一排的前n/2个排列与原来的相同),而且在第二排中加入编号n+1后会使第二排的某个编号值变大(因为第二排中的n+2不会被换出)。显然,无论第二排中哪个编号变大,只要重新按编号递增排列第二排, 都会保证原来 第二排比对应的第一排的人高(即编号较大),而第二排末尾的编号n+2总是能够确保大于第一排的末位;

 

因此,我们可以根据以上分析来画出从 编号为1 - n的排列编号为1 - (n+2)的排列 的状态转换树(n=2,4,6的情况):



以上状态转换树只画出了第一排的状态转换,对于第3层,它的n+1=5,n+2=6,显然n+2的值即为节点层序值的2倍

 

所以,求编号为1 - 12的排列总数,就是求该树的第6层的节点数,而从根节点1到第6层的每个节点的路径就是一个符合条件的第一排排列(可以用树的遍历算法求得)。 对于每个节点我们可以赋它的编号值为order和层序值为level,以下是简单计算第6层节点数的方法:

Java代码 复制代码  收藏代码
  1. private void firstOrder(int order, int level) {   
  2.     place.add(order);   
  3.     if (level >= 6) {   
  4.         count++;   
  5.         place.pop();   
  6.         return;   
  7.     }   
  8.   
  9.     for (int i = order + 1; i < 2 * (level + 1); i++)   
  10.         firstOrder(i, level + 1);   
  11.     place.pop();   
  12. }  
private void firstOrder(int order, int level) {
	place.add(order);
	if (level >= 6) {
		count++;
		place.pop();
		return;
	}

	for (int i = order + 1; i < 2 * (level + 1); i++)
		firstOrder(i, level + 1);
	place.pop();
}

以下是计算并打印第一排排列的完整代码:

Java代码 复制代码  收藏代码
  1. import java.util.Stack;   
  2.   
  3. public class TwoOrderQueue {   
  4.   
  5.     private int count = 0;   
  6.     private int total = 0;   
  7.     private Stack<Integer> place = new Stack<Integer>();   
  8.   
  9.     public TwoOrderQueue(int total) {   
  10.         this.total = total;   
  11.     }   
  12.   
  13.     private void firstOrder(int order, int level) {   
  14.         place.add(order);   
  15.         if (level >= total / 2) {   
  16.             //计数叶结点并打印路径   
  17.             count++;   
  18.             for (Integer i : place)   
  19.                 System.out.print(i + " ");   
  20.             System.out.println();   
  21.             place.pop();   
  22.             return;   
  23.         }   
  24.   
  25.         //展开编号为order的节点的子树,并进入下一层 level+1   
  26.         for (int i = order + 1; i < 2 * (level + 1); i++)   
  27.             firstOrder(i, level + 1);   
  28.         place.pop();   
  29.     }   
  30.   
  31.     public void firstOrder() {   
  32.         firstOrder(11);   
  33.         System.out.println("第一排一共有" + count + "种排列");   
  34.     }   
  35.   
  36.     public static void main(String[] args) {   
  37.         TwoOrderQueue q = new TwoOrderQueue(12);   
  38.         q.firstOrder();   
  39.     }   
  40. }  
import java.util.Stack;

public class TwoOrderQueue {

	private int count = 0;
	private int total = 0;
	private Stack<Integer> place = new Stack<Integer>();

	public TwoOrderQueue(int total) {
		this.total = total;
	}

	private void firstOrder(int order, int level) {
		place.add(order);
		if (level >= total / 2) {
			//计数叶结点并打印路径
			count++;
			for (Integer i : place)
				System.out.print(i + " ");
			System.out.println();
			place.pop();
			return;
		}

		//展开编号为order的节点的子树,并进入下一层 level+1
		for (int i = order + 1; i < 2 * (level + 1); i++)
			firstOrder(i, level + 1);
		place.pop();
	}

	public void firstOrder() {
		firstOrder(1, 1);
		System.out.println("第一排一共有" + count + "种排列");
	}

	public static void main(String[] args) {
		TwoOrderQueue q = new TwoOrderQueue(12);
		q.firstOrder();
	}
}

 

小结:

我们把12个高矮不同的人按身高递增的顺序排成1列,并依次从1到12编号,这里面除了简化问题之外,还存在什么样的思维切入点呢?我们把12个人分为2列,这里隐含了一个“有序”的方法,即假设有2个空队列,12个人依次选择是进入第1个队列还是第2个队列,最后只要两队人数相同即可。显然,这12个人进入队列的次序是可以任意的,那么我们为什么不选择一种有序的次序呢?而最明显的一种有序的次序就属身高递增的次序了,把他们依次编号的话不仅可以区分不同的身高,还表示了依次进入队列的次序,这样就最容易在加入队列的过程中找出规律。

我们都知道,计算机最擅长的就是使用规则来求解规范的问题 。比如单纯形法,第一步就是要把线性方程组规范化,而这里问题最好的规范化就是问题分析中的a步骤。

 

使用排列组合的方法求解(参见thinke365帖子中BenArfa的解答):

如果要满足题意,只要从12个人中挑选6个人放在第一排,那么所有的人的位置就都确定了,因为是要求按身高排序的。
这里我们的挑选方法以及限制条件是这样的:12个人先从矮到高排序,然后每个人被选到第一排或者第二排,如果要满足题意,当且仅当每挑选完一次之后,第二排中的人数不多于第一排中的人数,而这个条件的排列就是 C(12,6) - C(12,5) = 132 ,但这样并不能求得具体的排列次序。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
这道题可以使用回溯法来解决。我们可以先将 N 个人按照身矮到排序,然后将其分成两组,每组有 N/2 个人。接着,我们从第一组中选择一个人作为第一排的第一个人,从第二组中选择一个比他的人作为第二排的第一个人,然后递归地考虑剩下的人。 具体地,我们可以定义一个函数 `backtrack`,它的参数包括两个数组 `first` 和 `second`,分别表示第一排第二排已经选择的人;以及一个整数 `n`,表示还需要从第一组和第二组中选择的人数。函数的实现如下: ```cpp void backtrack(vector<int>& first, vector<int>& second, int n) { if (n == 0) { // 所有人都已经选择完毕,输出结果 for (int i = 0; i < first.size(); i++) { cout << first[i] << " "; } cout << endl; for (int i = 0; i < second.size(); i++) { cout << second[i] << " "; } cout << endl; return; } // 从第一组中选择一个人作为第一排的下一个人 int start = first.empty() ? 0 : first.back() + 1; for (int i = start; i < N; i++) { // 从第二组中选择一个比他的人作为第二排的下一个人 for (int j = 0; j < second.size(); j++) { if (height[i] > height[second[j]]) { // 符合条件,将这两个人加入第一排第二排,继续递归 first.push_back(i); second.push_back(j); backtrack(first, second, n - 1); // 回溯,撤销选择 first.pop_back(); second.pop_back(); } } } } ``` 我们可以在主函数中调用 `backtrack` 函数,如下所示: ```cpp int main() { vector<int> first, second; backtrack(first, second, N/2); return 0; } ``` 完整代码如下:

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值