#include<stdio.h>
#include<assert.h>
class CPrefixSorting
{
public:
CPrefixSorting()
{
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_SwapArray[i]);
printf("\n |Search Times| : %d \n", m_nSearch); //索引次數
printf("Total Swap Times = %d \n", m_nMaxSwap); //翻轉次數
}
private:
void Init(int* pCakeArray, int pCakeCnt)
{
assert(pCakeArray != NULL);
assert(pCakeCnt > 0);
m_nCakeCnt = pCakeCnt;
//初始化烙餅數組
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 = UpBound(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 UpBound(int nCakeCnt)
{
return nCakeCnt*2;
}
//尋找目前翻轉的下界
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_SwapArray[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 < 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_CakeArray; //烙餅資訊數組
int m_nCakeCnt; //烙餅個數
int m_nMaxSwap; //最多交換次數
int* m_SwapArray;//交換結果數組
int* m_ReverseCakeArray;//目前翻轉烙餅資訊數組
int* m_ReverseCakeArraySwap;//目前翻轉烙餅交換結果數組
int m_nSearch;//目前搜尋次數資訊
};
int main()
{
CPrefixSorting *l=new CPrefixSorting();
int aa[10]={ 3,2,1,6,5,4,9,8,7,0};
l->Run(aa,10);
l->Output();
return 0;
}
結果在編譯時存在以下問題:
1) Assert 應該是 assert
2) m_arrSwap 未被定義,應該改為m_SwapArray
3 )Init函數兩個for循環,後一個沒定義變量i,應該将i 改為 int i
另外,每運作一次Run函數,就會調用Init函數,就會申請新的記憶體,但卻沒有釋放原來的記憶體,會造成記憶體洩漏。if(step + nEstimate > m_nMaxSwap) 這句還會造成後面對m_ReverseCakeArraySwap數組的越界通路,使程式不能正常運作。
書上程式的低效主要是由于進行剪枝判斷時,沒有考慮好邊界條件,可進行如下修改:
1 ) if(step + nEstimate > m_nMaxSwap) >改為 >=。
2 ) 判斷下界時,如果最大的烙餅不在最後一個位置,則要多翻轉一次,因而在LowerBound函數return ret; 前插入行:
if (pCakeArray[nCakeCnt-1] != nCakeCnt-1)
ret++;
3 ) n個烙餅,翻轉最大的n-2烙餅最多需要2*(n-2)次,剩下的2個最多1次,因而上限值為2*n-3,是以,m_nMaxSwap初始值可以取2*n-3+1=2*n-2,這樣每步與m_nMaxSwap的判斷就可以取大等于号。
4 )采用書上提到的确定“上限值”的方法,直接建構一個初始解,取其翻轉次數為m_nMaxSwap的初始值。
1和2任改一處,都能使搜尋次數從172126降到兩萬多,兩處都改,搜尋次數降到3475。若再改動第3處,搜尋次數降到2989;若采用4的方法(此時初始值為10),搜尋次數可降到1045。