排序算法总结分析(三)——吃货排序之烙饼排序
来源:互联网 发布:数据恢复 编辑:程序博客网 时间:2024/06/11 02:40
目录
排序算法总结分析(一)——开篇
排序算法总结分析(二)——常见八大排序算法
排序算法总结分析(三)——吃货排序之烙饼排序
今天先来个好玩点的,呃,确切说是好吃的点的问题。哈哈,就是如标题表明的烙饼排序。程序猿果然思维跟普通人就不一样,连吃个饼都想的这么多。问题描述是这样的:把一摞饼按照大小次序摆好,要求是小的在上面,大的在下面,只能通过翻转一摞饼进行排序,就像用铲子插入某个位置,把这个位置之上的所有饼进行翻转。那假设有N块大小不一的烙饼,最少要翻转几次才能达到最终有序排列呢?
翻转演示图
与传统排序不同的是,不能一张张抽出来,然后插入进去;也不能任意交换两块饼。说明基本的排序算法都不太好用。是不是有点意思呢?想当年比尔·盖茨也研究过这个问题~
目前这个问题的答案只有一个范围,没有确切的值,完成排序需要的最少次数在(15/14)N与(18/11)N之间。2011年的时候这个问题被定义为NP-Hard。什么是NP-Hard?NP(Non-Deterministic)官方定义是非确定性多项式。而非确定性是指,可用一定数量的运算去解决多项式时间内可解决的问题。例如,著名的推销员旅行问题(Travel Saleman Problem or TSP):假设一个推销员需要从香港出发,经过广州,北京,上海,…,等 n 个城市,最后返回香港。任意两个城市之间都有飞机直达,但票价不等。假设公司只给报销C元钱,问是否存在一个行程安排,使得他能遍历所有城市,而且总的路费小于C?推销员旅行问题显然是NP的。因为如果你任意给出一个行程安排,可以很容易算出旅行总开销。但是,要想知道一条总路费小于C的行程是否存在,在最坏情况下,必须检查所有可能的旅行安排!这将是个天文数字。
好了介绍性的东西说的差不多了,下面主要讲一下怎么个排法~
由于每次操作都是针对最上面的饼,如果最底层的饼已经排好序,然后就只需要处理上面的N-1个饼了。
翻转图
首先,经过两次翻转,最大的饼已经在最下面了。接着次大的饼也需要两次翻转,最后剩两张饼的时候只需要1次就可以,所以总的至少翻转次数为2(n-1)-1即2n-3。
当然这是这个问题解的一种上限,非最优。
那么下限呢?这里也只说一种简单的优化,非最佳。每一次翻转最多使得一个烙饼与大小跟它相邻的烙饼排到一起。如果当前状态N个烙饼中,有M对相邻的烙饼它们不相邻,那么至少需要M次才能排好。
下面稍微介绍下优化方法及算法的实现。
假如这堆烙饼中有好几个不同的部分相对有序,就可以先把小一些的烙饼翻转使其有序。这样就会减少翻转次数。可以考虑每次翻转的时候,把两个本来应该相邻的烙饼尽可能换到一起。这样,当所有的烙饼都换到一起之后。实际上就完成了排序。这样的话就会想到使用动态规划或者递归的方法来实现。可以从不同的翻转策略开始,递归所有可能性。这样,肯定能找到最优解。
代码如下:
#include <stdio.h>/************************************************************************//* 烙饼排序实现——By Sin_Geek 2014.04.12 *//************************************************************************/class CPancakeSorting{public:CPancakeSorting(){m_nCakeCnt = 0;m_nMaxSwap = 0;}//计算烙饼翻转信息,pCakeArray 存储烙饼索引数组,nCakeCnt烙饼个数void Run(int* pCakeArray, int nCakeCnt){Init(pCakeArray, nCakeCnt);m_nSearch = 0;Search(0);}//输出烙饼具体翻转的次数void Output(){for (int i = 0; i < m_nMaxSwap; i++){printf("%d", m_arrSwap[i]);}printf("\nSearch Times : %d\n", m_nSearch);printf("Total Swap times = %d\n", m_nMaxSwap);}private://初始化数组信息,pCakeArray 存储烙饼索引数组,nCakeCnt烙饼个数void Init(int* pCakeArray, int nCakeCnt){m_nCakeCnt = nCakeCnt; //初始化m_CakeArray = new int[m_nCakeCnt];for (int i = 0; i < m_nCakeCnt; i++){m_CakeArray[i] = pCakeArray[i];}//设置最多交换次数信息m_nMaxSwap = UpBound(m_nCakeCnt);//初始化交换结果数组m_SwapArray = new int[m_nMaxSwap];//初始化中间交换结果信息m_ReverseCakeArray = new int[m_nCakeCnt];for (i = 0; i < m_nCakeCnt; i++){m_ReverseCakeArray[i] = m_CakeArray[i];}m_ReverseCakeArraySwap = new int[m_nMaxSwap];}//寻找当前翻转的上界int UpBound(int nCakeCnt){return nCakeCnt*2 - 3;}//寻找当前翻转的下界int LowerBound(int* pCakeArray, int nCakeCnt){int t,ret = 0;//根据当前数组的排序信息情况判断最少需要交换多少次for (int i = 1; i < nCakeCnt; i++){//判断位置相邻的两个烙饼是否为尺寸排序上相邻的t = pCakeArray[i] - pCakeArray[i-1];if ((t == 1) || (t == -1)){}else{ret++;}}return ret;}//排序的主函数void Search(int step){int i,nEstimate;m_nSearch++;//估算这次搜素所需的最小交换次数nEstimate = LowerBound(m_ReverseCakeArray, m_nCakeCnt);if (step + nEstimate > m_nMaxSwap) return;//如果已经排好序,输出结果if (IsSorted(m_ReverseCakeArray, m_nCakeCnt)) {if (step < m_nMaxSwap) {m_nMaxSwap = step;for (i = 0; i < m_nMaxSwap; i++)m_arrSwap[i] = m_ReverseCakeArraySwap[i];}return;}//递归翻转for (i = 1; i < m_nCakeCnt; i++){Revert(0,i);m_ReverseCakeArraySwap[step] = i;Search(step + 1);Revert(0,i);}}bool IsSorted(int* pCakeArray, int nCakeCnt){for (int i = 1; i < m_nCakeCnt; i++){if(pCakeArray[i-1] > pCakeArray[i])return false;}return true;}//翻转烙饼void Revert(int nBegin, int nEnd){//ASSERT(nEnd > nBegin);int i,j,t;for (i = nBegin, j = nEnd; i < j; i++,j--){t = m_ReverseCakeArray[i];m_ReverseCakeArray[i] = m_ReverseCakeArray[j];m_ReverseCakeArray[j] = t;}}private:int m_nCakeCnt; //烙饼个数int m_nMaxSwap; //最多交换次数int m_nSearch; //当前搜索次数int* m_CakeArray; //烙饼信息数组int* m_SwapArray; //交换结果数组int* m_ReverseCakeArray;//当前翻转烙饼信息数组int* m_ReverseCakeArraySwap;//当前翻转烙饼交换结果数组int m_arrSwap[10];};void main(){int nCakeCnt = 10;int arrSwap[10] = {3,2,1,6,5,4,9,8,7,0} ;CPancakeSorting pan;pan.Run(arrSwap, nCakeCnt);pan.Output();}
当然还可以把这个问题更复杂化一点,假定每个烙饼都有一面是烤过的,在原来排序的结果上附加一个条件,就是让所有烤过的那一面都朝下~有兴趣的可以思考一下哦~~
- 排序算法总结分析(三)——吃货排序之烙饼排序
- 【算法C++】烙饼排序
- 【经典算法】:烙饼排序
- 《编程之美》烙饼排序算法学习
- Java排序算法总结之(三)——选择排序(简单选择排序、堆排序)
- 烙饼排序
- 烙饼排序
- 烙饼排序
- 排序算法之——直接选择排序(三)
- 编程之美笔记——烙饼的排序
- 编程之美——烙饼排序问题
- 排序算法总结(三)插入排序
- 排序问题——翻转烙饼
- 排序算法总结(三)——堆排序、桶排序
- Java排序算法总结之(一)——插入排序(直接插入排序、折半插入排序、希尔排序)
- 排序算法(三)之插入排序
- 排序总结(三)之---堆排序
- 算法分析之排序:交换排序之二——快速排序(QuickSort)
- 申请美国密歇根州立大学需要具备哪些条件?
- CImageList使用指南
- 7-6 人数不定的工资类
- Android通过Jsoup解析Html源码
- 单链表很类似于栈,先插入的后遍历,删除也是删除最后插进来的。但增加的指定查找和指定删除的方法增加了灵活性,可以删除、查找指定key
- 排序算法总结分析(三)——吃货排序之烙饼排序
- Android学习资料整理
- java使用redis
- 冰心吴文藻的爱情始于留学远洋客轮上
- 安装minicom
- 水-百鸡百钱
- ubuntu OPENCV移植
- Android Performance(5) 模拟屏幕
- 通过指纹识别模块验证用户,解锁手机