编程之美-- 烙饼排序问题

来源:互联网 发布:oracle数据库性能优化 编辑:程序博客网 时间:2024/04/27 20:48

原文: http://blog.csdn.net/weixingstudio/article/details/6912434

问题描述: 有一摞烙饼,因为一只手端着盘子,所以只能用另外一只手来给烙饼排序,将烙饼由大到小排好序。这样就要求我们在给烙饼排序的时候总是将最上面的N个烙饼一起翻转。如果最下面的烙饼是最大的,那么只需要解决上面的N-1个烙饼,同理可以最后到解决两个烙饼的排序。

 

简单的排序方法:先找到最大的烙饼,将其和其以上的烙饼一起翻转,这样最大的烙饼就在盘子的最上面了,然后翻转所有的烙饼,这样最大的烙饼就在盘子的最下面了。同样的,处理剩下的N-1个烙饼,知道最后所有的烙饼都排好序。可以知道我们最多需要进行2*(N-1)次翻转就可以将所有的烙饼排好序。

 

问题? 如果减少烙饼的反转次数,达到一个最优的解。 加入这堆老兵中有几个不同的部分相对有序,凭直接来猜测,可以把笑一下的烙饼进行翻转,每次考虑翻转烙饼的时候总是把相邻的烙饼尽可能的放到一起。

 

解决方法: 通过穷举法来找所有可能方案中的最优方案,自然会用到动态规划或者递归的方法来实现。可以从不同的翻转策略开始,比如第一次先翻最小的,然后递归的把所有的可能都全部翻转一次。

 

既然2(N-1)是一个最多的翻转次数,就可以得知,如果算法中的翻转次数超过了2(N-1),我们就应该放弃这个算法。

 

我们总是希望UpperBound越小越好,而LowerBound则越大越好,这样就可以尽可能的减少搜索的次数,是算法的性能更好。

 

代码如下:(代码可能需要仔细的阅读,才能明白算法的含义)

 

[html] view plaincopy
  1. #pragma once  
  2. class CPrefixSorting  
  3. {  
  4. public:  
  5.     ~CPrefixSorting(void);  
  6.     CPrefixSorting()  
  7.     {  
  8.         m_nCakeCnt=0;  
  9.         m_nMaxSwap=0;  
  10.     }  
  11.   
  12.     // 计算烙饼翻转信息  
  13.     // @param  
  14.     // pCakeArray  存储烙饼索引数组  
  15.     // nCakeCnt    烙饼个数  
  16.     //   
  17.     void Run(int* pCakeArray, int nCakeCnt)  
  18.     {  
  19.         Init(pCakeArray,nCakeCnt);  
  20.         m_nSearch=0;  
  21.         Search(0);  
  22.     }  
  23.   
  24.     // 输出烙饼的翻转次数,翻转信息  
  25.     void Output()  
  26.     {  
  27.         for(int i=0;i<m_nMaxSwap;i++)  
  28.         {  
  29.             printf("%d",m_SwapArray[i]);  
  30.         }  
  31.         printf("\n |Search Times|:%d\n",m_nSearch);  
  32.         printf("Total Swap times=%d\n",m_nMaxSwap);  
  33.     }  
  34.   
  35. private:  
  36.     int* m_CakeArray;    // 烙饼信息数组  
  37.     int m_nCakeCnt;      // 烙饼的个数  
  38.     int m_nMaxSwap;      // 最多交换次数,根据前面的推断,最多为m_nCakeCnt*2  
  39.     int* m_SwapArray;    // 交换结果数组  
  40.     int* m_ReverseCakeArray;     // 当前翻转烙饼信息数组  
  41.     int* m_ReverseCakeArraySwap; // 当前翻转烙饼交换结果数组  
  42.     int m_nSearch;               // 当前搜索次数信息  
  43.   
  44.     // 初始化数组信息  
  45.     // @param  
  46.     // pCakeArray   存储烙饼索引数组  
  47.     // nCakeCnt     烙饼个数  
  48.     //  
  49.     void Init(int* pCakeArray,int nCakeCnt)  
  50.     {  
  51.         m_nCakeCnt=nCakeCnt;  
  52.   
  53.         // 初始化烙饼数组  
  54.         m_CakeArray=new int[m_nCakeCnt];  
  55.         for(int i=0;i<m_nCakeCnt;i++)  
  56.         {  
  57.             m_CakeArray[i]=pCakeArray[i];  
  58.         }  
  59.   
  60.         // 设置最多交换次数信息  
  61.         m_nMaxSwap=UpperBound(m_nCakeCnt);  
  62.   
  63.         // 初始化交换结果数组  
  64.         m_SwapArray=new int[m_nMaxSwap+1];  
  65.   
  66.         // 初始化中间交换结果信息  
  67.         m_ReverseCakeArray=new int[m_nCakeCnt];  
  68.         for(int i=0;i<m_nCakeCnt;i++)  
  69.         {  
  70.             m_ReverseCakeArray[i]=m_CakeArray[i];  
  71.         }  
  72.         m_ReverseCakeArraySwap=new int[m_nMaxSwap];  
  73.     }  
  74.   
  75.     // 翻转上届  
  76.     int UpperBound(int nCakeCnt)  
  77.     {  
  78.         return nCakeCnt*2;  
  79.     }  
  80.   
  81.     // 当前翻转的下届  
  82.     int LowerBound(int* pCakeArray, int nCakeCnt)  
  83.     {  
  84.         int t,ret=0;  
  85.         // 根据当前数组的排序信息情况来判断最少需要交换多少次  
  86.         for(int i=1;i<nCakeCnt;i++)  
  87.         {  
  88.             // 判断位置相邻的两个烙饼是否为尺寸排序上相等的  
  89.             t=pCakeArray[i]-pCakeArray[i-1];  
  90.             if((t==1)||(t==-1))  
  91.             {}  
  92.             else  
  93.             {  
  94.                 ret++;  
  95.             }  
  96.         }  
  97.   
  98.         return ret;  
  99.     }  
  100.   
  101.     // 排序的主函数  
  102.     void Search(int step)  
  103.     {  
  104.         int i, nEstimate;  
  105.         m_nSearch++;  
  106.   
  107.         // 估算当前搜索所需要的最小的交换次数  
  108.         nEstimate=LowerBound(m_ReverseCakeArray,m_nCakeCnt);  
  109.         if(step+nEstimate>m_nMaxSwap)  
  110.         {  
  111.             return;  
  112.         }  
  113.   
  114.         // 如果已经排好序,即翻转完成后,输出结果  
  115.         if(IsSorted(m_ReverseCakeArray, m_nCakeCnt))  
  116.         {  
  117.             if(step<m_nMaxSwap)  
  118.             {  
  119.                 m_nMaxSwap=step;// 修改最大的翻转次数,让m_nMaxSwap记录最小的翻转次数  
  120.                 for(i=0;i<m_nMaxSwap;i++)  
  121.                 {  
  122.                     m_SwapArray[i]=m_ReverseCakeArraySwap[i];  
  123.                 }  
  124.             }  
  125.             return;  
  126.         }  
  127.   
  128.         // 递归进行翻转  
  129.         for(i=1;i<m_nCakeCnt;i++)  
  130.         {  
  131.             Reverse(0,i);  
  132.             m_ReverseCakeArraySwap[step]=i;  
  133.             Search(step+1);  
  134.             Reverse(0,i);  
  135.         }  
  136.     }  
  137.   
  138.     // 判断是否已经排好序  
  139.     bool IsSorted(int* pCakeArray, int nCakeCnt)  
  140.     {  
  141.         for(int i=1;i<nCakeCnt;i++)  
  142.         {  
  143.             if(pCakeArray[i-1]>pCakeArray[i])  
  144.             {  
  145.                 return false;  
  146.             }  
  147.         }  
  148.         return true;  
  149.     }  
  150.   
  151.     // 翻转烙饼信息  
  152.     // 非常经典的数组翻转算法  
  153.     void Reverse(int nBegin,int nEnd)  
  154.     {  
  155.         int i,j,t;  
  156.         for(i=nBegin,j=nEnd;i<j;i++,j--)  
  157.         {  
  158.             t=m_ReverseCakeArray[i];  
  159.             m_ReverseCakeArray[i]=m_ReverseCakeArray[j];  
  160.             m_ReverseCakeArray[j]=t;  
  161.         }  
  162.     }  
  163. };  

[html] view plaincopy
  1. // CPrefixSorting.cpp : Defines the entry point for the console application.  
  2. //  
  3.   
  4. #include "stdafx.h"  
  5. #include "stdio.h"  
  6. #include "iostream"  
  7. #include "PrefixSorting.h"  
  8.   
  9. using namespace std;  
  10.   
  11.   
  12. int _tmain(int argc, _TCHAR* argv[])  
  13. {  
  14.     cout<<"--->begin"<<endl;  
  15.     CPrefixSorting panCakeSort;  
  16.     int panCake[5]={3,5,1,4,2};  
  17.     panCakeSort.Run(panCake,5);  
  18.     panCakeSort.Output();  
  19.     return 0;  
  20. }  


 

 

[html] view plaincopy
  1. // CPrefixSorting.cpp : Defines the entry point for the console application.  
  2. //  
  3.   
  4. #include "stdafx.h"  
  5. #include "stdio.h"  
  6. #include "iostream"  
  7. #include "PrefixSorting.h"  
  8.   
  9. using namespace std;  
  10.   
  11.   
  12. int _tmain(int argc, _TCHAR* argv[])  
  13. {  
  14.     cout<<"--->begin"<<endl;  
  15.     CPrefixSorting panCakeSort;  
  16.     int panCake[5]={3,5,1,4,2};  
  17.     panCakeSort.Run(panCake,5);  
  18.     panCakeSort.Output();  
  19.     return 0;  
  20. }  


输出的结果为:

[html] view plaincopy
  1. --->begin  
  2. 32423  
  3.  |Search Times|:2189  
  4. Total Swap times=5  
  5. 请按任意键继续. . .