编程之美读书笔记:翻烙饼的问题

原创 2013年12月04日 21:57:46

问题的描述:一摞大小无序的饼,允许单个或者多个一起直接的颠倒,求最优化的翻饼过程。

刚看到题目的时候,感觉比较的简单,就想一个数组,加上for循环,再加上几个条件判断就好了。只是一个程序,丝毫没有算法什么的概念,或者说就是为了解题而解题。看了书上面的分析,有点看不大懂的感觉,细细的看,才觉得这个才是问题沉淀的一个过程,或者说这样才是积累的一个过程。就比如高中老师说的那样:会一道题,算什么,要根据一道题会一片。

首先是,分析的一个过程,一般来说对于问题的,穷举都能有一定的帮助,对于问题的理解和发现其中的规律。现在是我们想到是首先找到最大的数,把它翻转到最下面。然后顺着这个思路进行分析。


粗略的搜索最优的解决的方案,是首把所有的可能的翻转方案过一篇,记录者最小的,利用的是递归的实现,递归的结束条件,一个是翻转的次是必须小于分析的最大值,一个是一个完成了。


java代码实现:

public class CakeTurn {

	public int m_nCakeCnt ;//烙饼的个数
	public int[] m_cakeArray ;//烙饼的半径的数组
	public int m_nMaxSwap;//最大的反转的次数
	public int[] m_swapArray;//交换的结果的数组
	public int[] m_reverseCakeArray;//当前翻转烙饼信息数组
	public int[] m_reverseCakeArraySwap;//当前翻转烙饼交换结果数组
	int m_nSearch;//当前搜索次数信息
	
	public void run(int[] cakeArray,int cakeNumber){
		//初始化数组
		init(cakeArray,cakeNumber);
		m_nSearch = 0;
		search(0);
	}

	public void output(){
		for (int i = 0; i < m_nMaxSwap; i++) {
			System.out.print("  "+m_swapArray[i]);
		}
		
		System.out.println(" searchTimes: " + m_nSearch); 
		System.out.println(" Total swap times : " + m_nMaxSwap);
	}
	//搜索
	private void search(int step) {
		//首先是搜索的步骤,每一步的搜索都记录,用于后面的计算
		 int i, nEstimate;
	        m_nSearch++;
	        
	        //剪枝条件,估计这个剪枝的判断,是相邻的数字不计入翻转的次数的,
	        nEstimate = lowerBound(m_reverseCakeArray);
	        if( step + nEstimate > m_nMaxSwap || step >= m_nMaxSwap)
	            return;
	        //是否翻转完成
	        if( isSorted( m_reverseCakeArray)){
	            if( step < m_nMaxSwap){//取翻转的次数
	                m_nMaxSwap = step;
	                for(i = 0; i < m_nMaxSwap; i++)
	                    m_swapArray[i] = m_reverseCakeArraySwap[i];//记录翻转的结果
	            }
	            return;
	        }
	        
	        for(i = 1; i < m_nCakeCnt; i++){
	            revert(0,i);
	            System.out.println(Arrays.toString(m_reverseCakeArray));//打印翻转过程中的,过程的变化
	            m_reverseCakeArraySwap[step] = i;
	            search(step+1);
	            revert(0,i);//并不是一开始找到了能够翻转好的方法,就完了,而是要确定最小的翻转的次数。
	        }
	        
	    }

	private void revert(int begine, int end) {
		int i = 0,j=0,t=0;
		//翻转烙饼的信息
		for (i = begine,j= end ; i < j; i++,j--) {
			t = m_reverseCakeArray[i];
			m_reverseCakeArray[i] = m_reverseCakeArray[j];
			m_reverseCakeArray[j] = t;
		}
		
	}

	//判断是否已经排好了续
	private boolean isSorted(int[] reverseCakeArray) {
		for (int i = 1; i < reverseCakeArray.length; i++) {
			if(reverseCakeArray[i-1] > reverseCakeArray[i]){
				return false;
			}
		}
		return true;
	}

	//估计这次搜索所需要的最小的交换的次数
	private int lowerBound(int[] cakeArray) {
		int t,ret = 0;
		//根据当前数组的排列顺序来判断最少需要交换多少次
		for(int i=1;i<cakeArray.length;i++){
			t = cakeArray[i]- cakeArray[i-1];
			if(t==-1||t==1){
				
			}else{
				ret++;
			}
		}
		return ret;
	}

	//初始化烙饼的信息:半径的信息组和烙饼的数量
	public void init(int[] cakeArray2, int cakeNumber2) {
		m_nCakeCnt = cakeNumber2;
		m_cakeArray = new int[m_nCakeCnt];
		for(int i = 0 ; i< cakeArray2.length;i++){
			m_cakeArray[i] = cakeArray2[i];
		}
		
		//最大的次数的获得
		m_nMaxSwap = upperBound(m_nCakeCnt);
		
		//设置初始化交换结果的数组
		m_swapArray = new int[m_nMaxSwap];
		
		//初始化中间交换结果的信息
		m_reverseCakeArray = new int[m_nCakeCnt];
		for (int i = 0; i < m_nCakeCnt; i++) {
			m_reverseCakeArray[i] = m_cakeArray[i];
		}
		m_reverseCakeArraySwap = new int[m_nMaxSwap];
		
	}

	//寻找当前翻转的最大值
	private int upperBound(int cakeNumber2) {
		return cakeNumber2*2;
	}
	
	public static void main(String[] args){
		CakeTurn problem = new CakeTurn();
//		int[] cakeArray = {3,2,1,6,5,4,9,8,7,0};
//		int[] cakeArray = {3,2,1,6,5,4};
		int[] cakeArray = {3,2,1};
        problem.run(cakeArray,cakeArray.length);
        problem.output();
	}
}
看到这个逻辑的实现,还是感觉比较的舒服的。最重要的是理解,这段代码:

 for(i = 1; i < m_nCakeCnt; i++){
	            //任何的时候都是从第一个到第几个开始翻转,所以首先得是从1到length-1来遍历,表现在程序中,就是i
	            revert(0,i);
	            System.out.println(Arrays.toString(m_reverseCakeArray));//打印翻转过程中的,过程的变化
	            //记录每一步,我从一到第几块饼(i)的翻转,也就是翻转的过程的记录
	            m_reverseCakeArraySwap[step] = i;
	            //既然第一步是从一开始便利,那么相同的第二步的逻辑还是和第一步一样的,从上面数几块饼进行翻转。
	            //没有退出条件的话,会一直的循环下去。所以我们在search中有自己的推出的条件,剪枝和是否完成
	            search(step+1);
	            revert(0,i);//并不是一开始找到了能够翻转好的方法,就完了,而是要确定最小的翻转的次数。
	        }

最简单的{3,2,1} 结果打印出来是,打印的是翻转过程中烙饼信息的变化:

[2, 3, 1]
[3, 2, 1]
[2, 3, 1]
[3, 2, 1]
[2, 3, 1]
[3, 2, 1]
[1, 3, 2]
[1, 2, 3]
[1, 3, 2]
[3, 1, 2]
[2, 3, 1]
[1, 2, 3]
[1, 3, 2]
[3, 1, 2]
[2, 3, 1]
[1, 2, 3]
  2 searchTimes: 17
 Total swap times : 1

首先是,根据结果集能够,更好的理解程序,在者就是说,可以根据结果集,优化程序。书中提到了三种优化程序的方法,上限下压,下限上提,驱除重复的状态。

上限,从cakeNumber*2,变化为(cakeNumber-1)*2,

 4  8  6  8  4  9 searchTimes: 164872
 Total swap times : 6

变为:

 4  8  6  8  4  9 searchTimes: 141787
 Total swap times : 6

下限书上面有具体的说明。

最大下界[15n/14],最小的上界是:[(5n+5)/3]

有时间研究一下,比尔盖茨的论文:http://www.eecs.berkeley.edu/~christos/papers/GP79.pdf

关于如何发现状态重复的问题,如果使用 m_reverseCakeArray,可能是n!对于n较大的情况下,这个是不大现实的,可以利用比较简单的算法,圈定一定的空间来进行处理。这个算作是一个普通的想法吧,另外这中算法是分支限界法(即遍历+剪枝=分支限界),以后交流的时候,可能会用到

关于这个,这个问题的动态规划或者贪心算法,会在下一个慢慢的研究。




编程之美 - 烙饼问题

把一摞烙饼按大的在下,小的在上拍好,一只手一次只能抓住上面的几张饼,把它们上下颠倒个个。反复几次后把饼排好。 问把饼排好需要的最小的次数。...
  • wangzhiyu1980
  • wangzhiyu1980
  • 2015年12月31日 22:27
  • 494

编程之美读书笔记-一摞烙饼的顺序

题目:星期五的晚上,一帮同事在希格玛大厦附近的硬盘酒吧多喝了几杯,程序员多喝了几杯之后谈什么呢?自然是算法问题。有个同事说:“我以前在餐厅打工,顾客经常点非常多的烙饼。店里的烙饼大小不一,我习惯在到达...
  • qq_32400847
  • qq_32400847
  • 2016年09月09日 20:59
  • 262

《编程之美》之摞烙饼的排序问题

转载自http://blog.csdn.net/zuiaituantuan/article/details/6056601
  • u011388550
  • u011388550
  • 2014年05月08日 18:09
  • 438

编程之美学习笔记--一摞烙饼的排序

问题:假设有n块大小不一的烙饼,翻烙饼时只能从最上面的烙饼开始,一次抓住最上面的几块饼,把它们上下颠倒个儿,那么最少要翻多少次,才能够达到最后的大小有序?思路先上一张图,可以很好的说明思路: 假...
  • jiyangsb
  • jiyangsb
  • 2015年05月25日 15:41
  • 664

编程之美之扩展问题

参考链接:http://blog.csdn.net/wuyuegb2312/article/details/9896831 1.1 让CPU占用率曲线听你指挥 参考: http://blog.csd...
  • jerryzcx
  • jerryzcx
  • 2014年03月06日 23:33
  • 2517

编程之美读书笔记-买书问题

题目:在节假日的时候,书店一般都会做促销活动。由于《哈利波特》系列相当畅销,店长决定通过促销活动来回馈读者。上柜的《哈利波特》平装书系列中,一共有五卷。假设每一卷单独销售均需8欧元。如果读者一次购买不...
  • qq_32400847
  • qq_32400847
  • 2016年09月09日 20:01
  • 455

java编程之美(一)

实践编程已经有足足6年多时间,也算是有一定经验,经常在工作中遇到各种让人不爽的代码编写方式,今天忍不住要来唠叨下。 为什么叫编程之美? 在我看来,代码有丑陋难看和赏心悦目两类,当然还有介于两者之...
  • tang9140
  • tang9140
  • 2015年11月01日 18:28
  • 1036

编程之美-翻烙饼问题

翻烙饼问题前言 翻烙饼问题是非常经典的问题,星期五的晚上,一帮同事在希格玛大厦附近的“硬盘酒吧”多喝了几杯。程序员多喝了几杯之后谈什么呢?自然是算法问题。有个同事说: “我以前在餐馆打工,顾...
  • rebornyp
  • rebornyp
  • 2017年12月23日 14:41
  • 31

编程之美2.18—数组分割

题目: 有一个没有排序,元素个数为2N的正整数数组。要求把它分割为元素个数为N的两个数组,并使两个子数组的和最接近。 基本思想: 假设数组A[1..2N]所有元素的和是SUM。模仿动...
  • wtyvhreal
  • wtyvhreal
  • 2015年04月28日 11:37
  • 681

2014-04-19编程之美初赛题目及答案解析

第一题: 描述 一般来说,我们采用针孔相机模型,也就是认为它用到的是小孔成像原理。 在相机坐标系下,一般来说,我们用到的单位长度,不是“米”这样的国际单位,而是相邻像素的长度。而焦距...
  • kunlong0909
  • kunlong0909
  • 2014年04月19日 16:44
  • 4460
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:编程之美读书笔记:翻烙饼的问题
举报原因:
原因补充:

(最多只允许输入30个字)