`
whg333
  • 浏览: 8095 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

四色立方体回溯法(迭代和递归)

 
阅读更多

原题来自某公司的一道面试题:

 

You have four colored cubes. Each side of each cube is a single color,
and there are four colors: blue (B), red (R), green (G) and yellow (Y)
Describing the six faces as front, back, left, right, top, bottom, the
cube colors are:

Cube    Front    Back    Left    Right    Top    Bottom
 1          R          B        G       Y         B         Y
 2          R          G        G       Y         B         B
 3          Y          B        R       G         Y         R
 4          Y          G        B       R         R         R

The objective is to find ways to stack the four cubes as a vertical
column so that each side of the column is showing all four colors.

Use the language of your choice to write a program to find all
successful permutations.

 

大概意思是说给定4个指定好颜色的立方体,从第1个立方体开始垂直叠堆起来,即叠堆成拥有4层的垂直立方体,叠堆起来的垂直立方体需要确保每4面(4面即:左、前、右后,上和下这2面可以不考虑)都有不同的颜色。

 

思路解决:回溯尝试(迭代或者递归)法去尝试枚举所有情况直到满足条件。

关键点:

1、每个立方体共有4*4=16种翻转情况,需要存储下每个立方体的16中翻转情况,便于在不满足条件时换下一种翻转情况尝试;

2、在尝试当前层玩所有翻转情况后依旧不满足条件,则需要有一种回溯机制(即回退到原来的地方),继续尝试下一种可能;

3、定义了一个立方体类(Cube),属性关键的包括左、前、右、后、上、下这6个面的颜色,还有一个包括了16种翻转情况的Cube列表;然后再判断相邻2层的立方体,当有某一面的颜色相同时,就代表不满足条件,否则就代表满足条件;

 

具体实现代码(java)如下,分别使用了迭代和递归的方式去求解问题。

 

public class Test {

	private static final Cube[] cubes = { 
		new Cube(Color.R, Color.B, Color.G, Color.Y, Color.B, Color.Y),
		new Cube(Color.R, Color.G, Color.G, Color.Y, Color.B, Color.B), 
		new Cube(Color.Y, Color.B, Color.R, Color.G, Color.Y, Color.R),
		new Cube(Color.Y, Color.G, Color.B, Color.R, Color.R, Color.R), 
	};
	
	static{
		for(Cube cube:cubes){
			cube.generateRotations();
		}
	}

	private enum Color {
		B, R, G, Y;
	}

	private static class Cube {
		private final Color front; 
		private final Color back; 
		private final Color left; 
		private final Color right;
		private final Color top; 
		private final Color bottom;
		
		private String name = "";
		private List<Cube> rotations;
		private int rotationIndex;
		
		public Cube(Color front, Color back, Color left, Color right, Color top, Color bottom) {
			this.front = front;
			this.back = back;
			this.left = left;
			this.right = right;
			this.top = top;
			this.bottom = bottom;
		}
		public void resetRotation() {
			rotationIndex = 0;
		}
		public boolean hasRotation() {
			return rotationIndex < rotations.size();
		}
		public Cube rotation() {
			Cube result = rotations.get(rotationIndex);
			rotationIndex++;
			//System.out.println(nextIndex/*+":"+result*/);
			return result;
		}
		public boolean isOneSideColorSame(Cube cube){
			return front == cube.front
					|| back == cube.back
					|| left == cube.left
					|| right == cube.right;
		}
		@Override
		public String toString() {
//			return "Cube [front=" + front + ", back=" + back + ", left=" + left + ", right=" + right + ", top=" + top
//					+ ", bottom=" + bottom + "]";
			return "Cube [top=" + top + ", bottom=" + bottom + ", front=" + front + ", back=" + back + ", left=" + left
					+ ", right=" + right + "] "+name;
		}
		@Override
		public int hashCode() {
			final int prime = 31;
			int result = 1;
			result = prime * result + ((back == null) ? 0 : back.hashCode());
			result = prime * result + ((bottom == null) ? 0 : bottom.hashCode());
			result = prime * result + ((front == null) ? 0 : front.hashCode());
			result = prime * result + ((left == null) ? 0 : left.hashCode());
			result = prime * result + ((right == null) ? 0 : right.hashCode());
			result = prime * result + ((top == null) ? 0 : top.hashCode());
			return result;
		}
		@Override
		public boolean equals(Object obj) {
			if (this == obj)
				return true;
			if (obj == null)
				return false;
			if (getClass() != obj.getClass())
				return false;
			Cube other = (Cube) obj;
			if (back != other.back)
				return false;
			if (bottom != other.bottom)
				return false;
			if (front != other.front)
				return false;
			if (left != other.left)
				return false;
			if (right != other.right)
				return false;
			if (top != other.top)
				return false;
			return true;
		}
		public void generateRotations() {
			Set<Cube> rotationSet = new LinkedHashSet<Cube>();
			
			int i = 0;
			Cube current = null;
			do {
				current = i == 0 ? this : current.turnLeft90Degree();
				if(rotationSet.add(current)){
					current.name = i > 0 ? i + " left90Degree" : "";
					//System.out.println("i:"+i+" add "+left90Degree);
				}
				Cube down90Degree = current;
				for(int j=0;j<3;j++){	//内嵌的循环为3是为了减去一次不必要的重复情况 ,因为第一个面在之前已经add了
					down90Degree = down90Degree.turnDown90Degree();
					if(rotationSet.add(down90Degree)){
						down90Degree.name = (i > 0 ? i + " left90Degree and " : "") + (j + 1) + " down90Degree";
						//System.out.println("i:"+i+",j:"+j+" add "+down90Degree);
					}
				}
				i++;
			} while (rotationSet.size() < 16);	//立方体总的旋转共能产生4×4=16种情况
			
			rotations = new ArrayList<Cube>(rotationSet);
		}
		public Cube turnLeft90Degree(){
			return new Cube(right, left, front, back, top, bottom);
		}
		public Cube turnDown90Degree(){
			return new Cube(top, bottom, left, right, back, front);
		}
	}
	
	public static void main(String[] args) {
		solveByIterator();
		resetCubesRecursion();
		solveByRecursion();
		//print4CubeRotations();
	}
	
	private static void solveByIterator(){
		print(solveByIterator(4), "solveByIterator resolution");
	}
	
	private static List<Cube> solveByIterator(final int level){
		List<Cube> result = new ArrayList<Cube>();
		boolean isAddNextCube = false;
		Cube currentCube = cubes[0].rotation();
		for(int i=0;i<level;){
			if(isAddNextCube){
				currentCube = cubes[i].rotation();
				isAddNextCube = false;
			}
			
			if(addCube(result, currentCube)){
				i++;
				isAddNextCube = true;
			}else{
				while(!cubes[i].hasRotation()){	//当此层立方体的所有旋转情况都试过后依旧无解
					cubes[i].resetRotation();		//重置此层的旋转情况,为下次拿到下一个旋转情况做准备
					i--;							//返回上一层
					if(i < 0){
						throw new IllegalArgumentException(level+"层四色立方体无解!");
					}
					result.remove(i);	//因为下一层所有情况都尝试依旧无解,故重新清除之前已经存储的的立方体情况
				}
				currentCube = cubes[i].rotation(); //拿到下一个旋转情况再尝试
				//失败的话拿另一个立方体尝试,这里没有必要,因为假设立方柱的叠放所拿的立方体顺序为1、2、3、4
			}
		}
		return result;
	}
	
	private static void resetCubesRecursion(){
		for (Cube cube : cubes) {
			cube.resetRotation();
		}
	}
	
	private static void solveByRecursion(){
		List<Cube> result = new ArrayList<Cube>();
		final int level = 4;
		if(!solveByRecursion(level, cubes, cubes[0].rotation(), result)){
			throw new IllegalArgumentException(level+"层四色立方体无解!");
		}
		print(result, "solveByRecursion resolution");
	}
	
	private static boolean solveByRecursion(final int level, Cube[] cubes, Cube current, List<Cube> result){
		if(!tryAddCubeIterate(current, result)){
			return false;
		}
		
		if(result.size() == level){
			return true;
		}
		
		if(!solveByRecursion(level, cubes, cubes[result.size()].rotation(), result)){
			result.remove(result.size()-1);	//返回上一层
			if(!cubes[result.size()].hasRotation()){
				cubes[result.size()].resetRotation();
				return false;
			}
			return solveByRecursion(level, cubes, cubes[result.size()].rotation(), result);
		}
		return true;
	}
	
	/** 尝试该层的所有翻转情况,还不行则重置此层的旋转情况,为下次拿到下一个旋转情况做准备 */
	private static boolean tryAddCubeIterate(Cube current, List<Cube> result){
		boolean isAddSucc = addCube(result, current);
		while(!isAddSucc){
			if(!cubes[result.size()].hasRotation()){
				cubes[result.size()].resetRotation();
				return false;
			}
			isAddSucc = addCube(result, cubes[result.size()].rotation());
		}
		return true;
	}
	
	private static boolean addCube(List<Cube> result, Cube cube){
		if(!isSatisfy(result, cube)){
			return false;
		}
		return result.add(cube);
	}

	private static boolean isSatisfy(List<Cube> result, Cube cube) {
		if(result.isEmpty()){
			return true;
		}
		for(Cube ele:result){
			if(ele.isOneSideColorSame(cube)){
				return false;
			}
		}
		return true;
	}
	
	private static void print(List<Cube> result, String solveType) {
		System.out.println("----------------------------------"+solveType+"----------------------------------");
		for(int i=0;i<result.size();i++){
			Cube ele = result.get(i);
			System.out.println((i+1)+"_"+ele);
		}
		System.out.println("----------------------------------"+solveType+"----------------------------------");
	}
	
	private static void print4CubeRotations() {
		for (Cube cube : cubes) {
			System.out.println(cube.rotations.size());
			for (Cube c : cube.rotations) {
				System.out.println(c);
			}
		}
	}

}

 

  

代码在求解出一种情况后就会退出,并打印该情况下4个立方体的6个面和到达该面需要做的翻转:

 

----------------------------------solveByIterator resolution----------------------------------
1_Cube [top=G, bottom=Y, front=B, back=Y, left=R, right=B] 1 left90Degree and 1 down90Degree
2_Cube [top=B, bottom=B, front=Y, back=G, left=G, right=R] 3 left90Degree and 2 down90Degree
3_Cube [top=R, bottom=Y, front=G, back=R, left=B, right=Y] 3 left90Degree and 2 down90Degree
4_Cube [top=R, bottom=R, front=R, back=B, left=Y, right=G] 1 left90Degree
----------------------------------solveByIterator resolution----------------------------------
----------------------------------solveByRecursion resolution----------------------------------
1_Cube [top=G, bottom=Y, front=B, back=Y, left=R, right=B] 1 left90Degree and 1 down90Degree
2_Cube [top=B, bottom=B, front=Y, back=G, left=G, right=R] 3 left90Degree and 2 down90Degree
3_Cube [top=R, bottom=Y, front=G, back=R, left=B, right=Y] 3 left90Degree and 2 down90Degree
4_Cube [top=R, bottom=R, front=R, back=B, left=Y, right=G] 1 left90Degree
----------------------------------solveByRecursion resolution----------------------------------

 

 

求解出1种解的情况后,能横向翻转和纵向翻转,实际就有4*2=8种解的情况了。

 

分享到:
评论

相关推荐

    迭代与递归的区别

    在计算机编程中,迭代与递归是两种常用的解决重复性问题的方法。它们各自有不同的特点和适用场景。理解它们之间的区别,对于编写高效和优雅的代码至关重要。 迭代是一种方法,它通过重复执行一组指令来逐步逼近最终...

    n后问题---递归回溯法 n后问题---递归回溯法

    n后问题---递归回溯法 n后问题---递归回溯法 n后问题---递归回溯法 n后问题---递归回溯法 n后问题---递归回溯法 n后问题---递归回溯法 n后问题---递归回溯法 n后问题---递归回溯法 n后问题---递归回溯法 n后问题---...

    回溯法背包问题非递归实现

    回溯法递归实现和非递归实现.解用向量表示,解分量集合有1、2两个元素,一表示放入背包,二表示不放入背包。具有一般性。

    DNS迭代查询和递归查询的区别.docx

    "DNS 迭代查询和递归查询的区别" DNS(Domain Name System)是 Internet 中的一个基础设施,提供域名到 IP 地址的映射服务。在 DNS 解析过程中,查询类型是一个关键概念,有两种主要的查询类型:迭代查询和递归查询...

    迭代和递归1.2.pptx

    公司要求分享迭代和递归函数,在周末的时间整理了一个简单的PPT,在这里也分享给大家,互相学些,相互总结。

    迭代与递归算法

    例如,"递归和迭代的区别.doc"可能阐述了递归如何通过递归公式解决斐波那契序列或其他分治策略问题,如分治法的基本思想文档所讨论的那样。递归在解决某些问题时有其独特的优势,因为它能够简化代码结构,但需要注意...

    用回溯法递归求解八数码

    总结来说,八数码问题的解法通过回溯法结合递归策略,能够在大量的可能性中高效地寻找解决方案。这种算法适用于许多其他类似的组合优化问题,如数独、旅行商问题等。通过理解并掌握这种算法,我们可以更好地应对复杂...

    C++:斐波那契数列(迭代和递归)

    总结来说,C++提供了多种方式来实现斐波那契数列,包括迭代和递归,以及递归的优化形式——记忆化。在实际应用中,我们需要根据问题规模和性能需求选择合适的方法。对于小规模的计算,递归可能更为直观;而大型计算...

    oracle递归、迭代

    ### Oracle中的递归查询详解 #### 一、引言 在数据库管理中,处理具有层次结构的数据是一项常见的任务。例如,在组织结构、产品分类或文件系统等场景...希望本文能帮助读者更好地理解和应用Oracle中的递归查询技术。

    c语言-阶乘算法(迭代和递归).docx

    C语言-阶乘算法(迭代和递归) C语言-阶乘算法是计算阶乘的经典算法,包括迭代算法和递归算法两种实现方式。阶乘是数学中的一种运算符号,表示一个数字的所有正整数因子的乘积,如n! = n * (n-1) * (n-2) * (n-3) * ....

    回溯法求解子集和问题

    回溯法是一种强大的算法策略,常用于解决组合优化问题,如子集和问题。这个问题的目标是从给定的一组整数中找出所有可能的子集,使得这些子集的元素和等于一个特定的目标值。在本案例中,我们将深入探讨如何使用回溯...

    回溯法实验报告解装载问题

    回溯法是一种基于试探性的深度优先搜索的算法,常用于解决约束满足问题。在这个实验报告中,回溯法被应用于解决装载问题,具体是经典的N皇后问题,即在N×N的棋盘上放置N个皇后,使得每行、每列以及对角线上的皇后...

    C语言实现 求二项式各项系数(迭代,递归)

    本文将围绕“C语言实现求二项式各项系数(迭代,递归法)”这一主题,深入探讨如何利用C语言通过迭代和递归两种方法来计算二项式系数,这不仅体现了C语言的强大功能,也展示了编程中不同算法的运用与对比。...

    回溯法迭代

    递归回溯,像8皇后问题,本质上和多重for 循环是一样的

    回溯法实验n皇后问题迭代法.doc

    回溯法实验n皇后问题迭代法的设计主要包括两个部分:回溯法求解n皇后问题的思想和迭代法实现回溯搜索。 回溯法思想 回溯法是用于解决约束满足问题的搜索算法。其主要思想是通过回溯来找到满足约束条件的解。对于n...

    递归回溯法求解整数线性规划及MATLAB实现.pdf

    在一些教学和初步研究场合,递归回溯法的简洁性和直观性使其成为一个非常好的选择。而在求解复杂问题时,则可能需要考虑更为高效的算法,如分支定界法或其他更为高级的优化策略。不管选择何种方法,MATLAB平台都为...

    回溯法的着色问题.zip

    回溯法是一种在解决问题时,通过尝试所有可能的解决方案,并在发现某一步不满足条件时,退回一步重新选择的方法,以此来寻找问题的全局最优解。在这个“着色问题”的场景中,我们可以假设是经典的图着色问题或者棋盘...

    使用迭代和递归两种方法反转链表.docx

    ### 使用迭代和递归两种方法反转链表 在计算机科学中,链表是一种常见的数据结构,广泛应用于多种算法和数据处理场景中。反转链表是一个经典的面试题目,它不仅能够考察求职者的编程基础,还能检验其对递归的理解...

    迭代法、穷举搜索法、递推法、递归.....

    "迭代法、穷举搜索法、递推法、递归、回溯法、贪婪法、分治法、动态规划法" 迭代法是一种常用的算法设计方法,用于求方程或方程组的近似根。迭代法的基本思想是将方程转换为等价的迭代式,然后通过不断迭代,逐步...

Global site tag (gtag.js) - Google Analytics