問題:假設有n塊大小不一的烙餅,翻烙餅時只能從最上面的烙餅開始,一次抓住最上面的幾塊餅,把它們上下顛倒個兒,那么最少要翻多少次,才能夠達到最后的大小有序?
思路
先上一張圖,可以很好的說明思路:
假設有四張無序的餅,那么問題就變成了找到使層數最小的結點。書中給出的思路是:
將烙餅從第二張開始一個一個的嘗試去翻,采用深度優先搜索的策略。在搜索開始之前,先找到了一種完成任務的方式,最少需要2*(n_cake-1)步。這就是一個搜索的上界,如果搜索步驟超過了這個上界,則這一枝可以拋棄不用搜索了,如果搜索到了一種翻轉方式,則將這種翻轉方式的翻轉次數更新為新的搜索上界值,以減小搜索范圍;同時給出了計算搜索下界的計算方式:當前狀態翻轉到有序狀態的步驟數目下界:若當前狀態中,還有m對烙餅沒有相鄰,而每次翻轉最多只能使得一個烙餅與大小跟它相鄰的烙餅拍到一起,故至少還要m次才能排好,如果當前搜索步數加上這個下界的值超過了搜索上界值,則在該枝的搜索就不必進行了。
搜索時,循環進行每一個子樹的搜索,循環中使用遞歸方式搜索;程序如下:
#include <stdio.h>
#include <stdlib.h>class CakeSort
{
private:
int* m_CakeArray;
int m_CakeCount;
int m_MaxSwap;
int m_SwapTimes;
int* m_SwapArray;
int* m_ReverseCake;
int* m_SwapReverseCake;
int* m_SortArray;
public:
void Init(
int* pCakeArray,
int count);
void Run(
int* pCakeArray,
int count);
int UpperBound(
int count);
int LowerBound(
int* pArray,
int count);
void Search(
int step);bool IsSort(
int* pArray,
int count);
void Reverse(
int begin,
int end);~CakeSort(
void);
};
void CakeSort::Init(
int* pCakeArray,
int count)
{m_CakeCount =
count;m_CakeArray =
new int[
count];
for(
int i =
0 ; i <
count ; i++)m_CakeArray[i] = pCakeArray[i];m_ReverseCake =
new int[
count];
for(
int i =
0 ; i <
count ; i++)m_ReverseCake[i] = m_CakeArray[i];m_MaxSwap = UpperBound(
count);m_SwapTimes =
0;m_SwapArray =
new int[m_MaxSwap];m_SwapReverseCake =
new int[m_MaxSwap];m_SortArray=
new int [m_CakeCount];
}
int CakeSort::UpperBound(
int count)
{
return 2*(
count -
1);
}
int CakeSort::LowerBound(
int* pArray,
int count)
{
int ret =
0 ;
int t;
for(
int i =
1 ; i <
count ; i++){t = pArray[i] - pArray[i-
1];
if((t !=
1) && ( t!= -
1))ret++;}
return ret;
}
void CakeSort::Run(
int* pArray,
int count)
{Init(pArray,
count);Search(
0);printf(
"最優翻轉方式:\n");
for (
int i =
0; i < m_MaxSwap; i++) printf(
"%d\n", m_SwapArray[i]); printf(
"Search Times : %d\n", m_SwapTimes); printf(
"Total Swap times = %d\n", m_MaxSwap);
for (
int i=
0;i<m_CakeCount;i++){printf(
"cake:%d\n",m_SortArray[i]);}}
void CakeSort::Reverse(
int begin ,
int end)
{
int i,j,temp;
for(i = begin , j = end ; i < j ; i++ , j--){temp = m_ReverseCake[i];m_ReverseCake[i] = m_ReverseCake[j];m_ReverseCake[j] = temp;}
}
bool CakeSort::IsSort(
int* pArray,
int count)
{
for(
int i =
1 ; i <
count ; ++i){
if(pArray[i] < pArray[i-
1])
return false;}
return true;
}
void CakeSort::Search(
int step)
{m_SwapTimes++;
int Est = LowerBound(m_ReverseCake,m_CakeCount);
if(Est + step >= m_MaxSwap)
return;
if(IsSort(m_ReverseCake,m_CakeCount)){
if(step <= m_MaxSwap){m_MaxSwap = step;
for(
int i =
0 ; i < m_MaxSwap ; i ++){m_SwapArray[i] = m_SwapReverseCake[i];printf(
"%d\n",m_SwapArray[i]);}
for (
int i=
0;i<m_CakeCount;i++){m_SortArray[i]=m_ReverseCake[i];printf(
"cake:%d\n",m_ReverseCake[i]);}}
return;}
for(
int i =
1 ; i < m_CakeCount ; i++){Reverse(
0,i);m_SwapReverseCake[step] = i;Search(step+
1);Reverse(
0,i);}
}
int main()
{
int a[] = {
3,
2,
1,
6,
5,
4,
9,
8,
7,
0}; CakeSort s;s.Run(a,
10);system(
"pause");
return 0;
}CakeSort::~CakeSort(
void)
{
if(m_CakeArray!=NULL)delete []m_CakeArray;
if(m_SwapReverseCake!=NULL)delete []m_SwapReverseCake;
if(m_SwapArray!=NULL)delete []m_SwapArray;
if(m_ReverseCake!=NULL)delete []m_ReverseCake;
if(m_SortArray!=NULL)delete []m_SortArray;
}
從上述程序的輸出結果中可以看出,其實程序還是搜索了很多不必要的路徑,如剛剛將頭兩張烙餅翻轉,下一步馬上又把頭兩張餅翻轉過來,顯然是重復了,就沒有再向下搜索的必要了,因此可以繼續優化,進一步減小搜索范圍
這里我們采用的想法是:上一步翻轉過的前i張餅,在下一次迭代時,直接跳過翻轉前i張餅的情況(因為這樣又會恢復到上一步的狀態),因此我們將:Search遞歸函數修改為:
void CakeSort::Search(
int step,
int flapIndex)
{m_SwapTimes++;
int Est = LowerBound(m_ReverseCake,m_CakeCount);
if(Est + step >= m_MaxSwap)
return;
if(IsSort(m_ReverseCake,m_CakeCount)){
if(step <= m_MaxSwap){m_MaxSwap = step;
for(
int i =
0 ; i < m_MaxSwap ; i ++){m_SwapArray[i] = m_SwapReverseCake[i];
printf(
"%d\n",m_SwapArray[i]);}
for (
int i=
0;i<m_CakeCount;i++){m_SortArray[i]=m_ReverseCake[i];
printf(
"cake:%d\n",m_ReverseCake[i]);}}
return;}
for(
int i =
1 ; i < m_CakeCount ; i++){
if(flapIndex==i)
continue;Reverse(
0,i);m_SwapReverseCake[step] = i;Search(step+
1,i);Reverse(
0,i);}
}
相應的,run函數修改為:
void CakeSort::Run(
int* pArray,
int count)
{Init(pArray,count);Search(
0,
0);
//打印最優排序方式
printf(
"最優翻轉方式:\n");
for (
int i =
0; i < m_MaxSwap; i++)
printf(
"%d\n", m_SwapArray[i]);
printf(
"Search Times : %d\n", m_SwapTimes);
printf(
"Total Swap times = %d\n", m_MaxSwap);
//打印排序結果
for (
int i=
0;i<m_CakeCount;i++){
printf(
"cake:%d\n",m_SortArray[i]);}}
雖然可以避免一部分無用搜索,但是還是會存在無用的搜索。
總結
以上是生活随笔為你收集整理的编程之美学习笔记--一摞烙饼的排序的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。