一摞烙饼的问题
问题:
星期五的晚上,一帮同事在希格玛大厦附近的“硬盘酒吧”多喝了几杯。程序员多喝了几杯之后谈什么呢?自然是算法问题。有个同事说:“我以前在餐馆打工,顾客经常点非常多的烙饼。店里的饼大小不一,我习惯在到达顾客饭桌前,把一摞饼按照大小次序摆好——小的在上面,大的在下面。由于我一只手托着盘子,只好用另一只手,一次抓住最上面的几块饼,把它们上下颠倒个个儿,反复几次之后,这摞烙饼就排好序了。我后来想,这实际上是个有趣的排序问题:假设有n块大小不一的烙饼,那最少要翻几次,才能达到最后大小有序的结果呢?”
你能否写出一个程序,对于n块大小不一的烙饼,输出最优化的翻饼过程呢?
解决方案:
《编程之美》上面给出的方法是通过递归寻找出最优方案。
递归结束条件有两个:
1)递归步骤超过最大步骤数;
2)已经有序。
但由于递归过程步骤过多,需要对其进行剪枝:
1)定上界:假设每次只将最大的翻到最下面(跟冒泡类似),至多需要2次翻转,总共有n-1个需要翻转(最后一个不用翻了),那么最多需要2*(n-1)次翻转;当然,为了减少更多不必要的遍历,每次找到一个实现了有序的步骤数step,则将max更新为该step。
2)定下界:每次递归时,计算当前所在状态下至少还需要多少次翻转,设为lowerbound, 如果当前已经翻转的步骤数step+lowerbound(已经翻转步骤数+当前状态下至少还需要的步骤数)超过了上届,则可以结束该次递归了。
下面是结合该想法,自己写的一个程序:
//============================================================================ // Name : 3_version1.cpp // Author : yahohi // Version : // Copyright : All rights reserved // Description : Ansi-style //============================================================================ #include <iostream> using namespace std; #define size 5 int array[size] = {1,2,3,4,5}; int nMax = 2*(size - 1); static int move[2*(size - 1)]; bool isSorted(int len); void reverse(int begin, int end); void println(int a[], int len); int LowerBound(int a[], int s); void Search(int step) { int lowerbound = LowerBound(array, size); if ((lowerbound + step) > nMax) return; if (isSorted(size)) { cout << endl<< "step: " << step << endl; cout << "More details: " << endl; println(move, step); nMax = step; return; } //每一次都有size-1中翻的方法 for (int i = 1;i < size;i ++) { reverse(0,i);//翻前i个饼 move[step] = i; Search(step + 1); reverse(0,i);//恢复现场 } } int LowerBound(int a[], int s) { int ret = 0; for (int i = 0;i < s - 1; i ++) { if (abs(a[i] - a[i+1]) != 1) ret ++; } return ret; } bool isSorted(int len) { bool ret = true; for (int i = 0;i < len-1; i ++) { if (array[i] > array[i+1]) { ret = false; break; } } return ret; } void reverse(int begin, int end) { for (int i = begin;i < (begin+end + 1)/2; i++) { int temp = array[i]; array[i] = array[end - i]; array[end - i] = temp; } } void println(int a[], int len) { for (int i = 0;i < len; i ++) { cout << a[i] << " "; } cout<<endl; } int main() { reverse(0,1); reverse(0,2); println(array, size); Search(0); return 0; }其中,递归过程中的恢复现场尤为重要!
版权声明:本文为博主原创文章,未经博主允许不得转载。