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

来源:互联网 发布:如何提高免疫力 知乎 编辑:程序博客网 时间:2024/06/10 07:32

题目:星期五的晚上,一帮同事在希格玛大厦附近的硬盘酒吧多喝了几杯,程序员多喝了几杯之后谈什么呢?自然是算法问题。有个同事说:“我以前在餐厅打工,顾客经常点非常多的烙饼。店里的烙饼大小不一,我习惯在到达顾客饭桌前,把一摞饼按照大小次序摆好--小的在上面,大的在下面。由于我一只手托着盘子,只好用另一只手,一次抓住最上面的几块饼,把它们上下颠倒个个儿,反复几次之后,这摞烙饼就排好序了。我后来想,这实际上是个有趣的排序问题:假设有n块大小不一的摞饼,那最少要翻几次,才能达到大小有序的结果呢?”
解析:考虑用分支限界法解决。
上界:为了把最大的烙饼摆在最下面,我们先把最上面的和最大的烙饼之间翻转,这样,最大的烙饼就在最上面了。接着,我们把所有烙饼翻转,最大的烙饼就摆在最下面了。


之后,我们对上面n-1、n-2个饼重复这个过程。因此,最多需要2(n-1)次翻转就可以把所有烙饼排好序。
下界:下界是动态的,取值是当前排序状态下,我们至少还要翻转的次数,程序中用变量nEstimate表示。当当前翻转次数+nEstimate>上界时,说明继续遍历下去最后的结果会超过上界,这时我们直接对其进行剪枝。每一次翻转我们最多使得一个烙饼与大小跟它相邻的烙饼排到一起。如果当前状态n个烙饼中,有m对相邻的烙饼的半径不相邻,那么我们至少需要m次才能排好序。

#include <cassert>#include <iostream>using namespace std;class PrefixSorting{public:PrefixSorting(){m_nCakeCnt = 0;m_nMaxSwap = 0;}~PrefixSorting(){if (m_CakeArray != NULL) delete [] m_CakeArray;if (m_SwapArray != NULL) delete [] m_SwapArray;if (m_ReverseCakeArray != NULL) delete [] m_ReverseCakeArray;if (m_ReverseCakeArraySwap != NULL) delete [] m_ReverseCakeArraySwap;}void Run(int* pCakeArray, int nCakeCnt){Init(pCakeArray, nCakeCnt);Search(0);}void Output(){for (int i = 0; i < m_nMaxSwap; i++) printf("%d\n", m_SwapArray[i]);printf("Total Swap times = %d\n", m_nMaxSwap);}private:void Init(int* pCakeArray, int nCakeCnt){assert(pCakeArray != NULL);assert(nCakeCnt > 0);m_nCakeCnt = nCakeCnt;m_CakeArray = new int[m_nCakeCnt];assert(m_CakeArray != NULL);for (int i = 0; i < m_nCakeCnt; i++){m_CakeArray[i] = pCakeArray[i];}m_nMaxSwap = UpperBound(m_nCakeCnt);m_SwapArray = new int[m_nMaxSwap];assert(m_SwapArray != NULL);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];}//寻找当前翻转的上界int UpperBound(int nCakeCnt){return (nCakeCnt - 1) * 2;   }//寻找当前翻转的下界int LowerBound(int* pCakeArray, int nCakeCnt){int t;int ret = 0;for (int i = 1; i < nCakeCnt; i++){t = pCakeArray[i] - pCakeArray[i - 1];if (t != 1 && t != -1) ret++;}return ret;}//排序的主函数void Search(int step){int i, nEstimate;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_SwapArray[i] = m_ReverseCakeArraySwap[i];}return;}//递归进行翻转for (int i = 1; i < m_nCakeCnt; i++){Reverse(0, i);m_ReverseCakeArraySwap[step] = i;Search(step + 1);Reverse(0, i);}}//判断是否已经排好序bool IsSorted(int* pCakeArray, int nCakeCnt){for (int i = 1; i < nCakeCnt; i++){if (pCakeArray[i - 1] > pCakeArray[i]) return false;}return true;}//翻转烙饼void Reverse(int nBegin, int nEnd){assert(nEnd > nBegin);int i, j, t;for (i = nBegin, j = nEnd; i < j; i++, j--){swap(m_ReverseCakeArray[i], m_ReverseCakeArray[j]);}}int* m_CakeArray;//烙饼信息数组  int m_nCakeCnt;//烙饼个数  int m_nMaxSwap;//最多交换次数 int* m_SwapArray;//交换结果数组  int* m_ReverseCakeArray;        //当前翻转烙饼信息数组  int* m_ReverseCakeArraySwap;    //当前翻转烙饼交换结果数组  };int main(){int arr[] = { 3, 2, 1, 6, 5, 4, 9, 8, 7, 0 };PrefixSorting test;test.Run(arr, 10);test.Output();return 0;}

0 0
原创粉丝点击