前天在《編程之美》中讀到一個“一摞烙餅排序”的問題,第一眼不會做,看了答案之後倒是讓我明白了一年多之前不真正理解的剪枝搜索的內涵。記錄一下,作爲分享和溫習之地。
剪枝搜索說白了還是搜索,就像動態規劃說白了就是使用特殊的方法進行搜索以提高效率。剪枝搜索中涉及到很重要的概念:狀態空間和搜索樹。我覺得狀態空間和搜索樹之間是有絕對的關係的。剪枝搜索就是在一棵狀態樹上搜索想要的節點。然而和以前的樹不同的是,這棵樹中的節點並不是都在內存中的,而是到達該節點時該節點通過數組表示在內存中而其他節點並沒有,通過改變數組內容來達到狀態的改變。在程序中通過剪枝條件判斷進行中斷某條搜索路徑轉向其他路徑,從而不必在無用的路徑浪費時間,進而提高效率。在程序中,典型的是在一個循環中存在遞歸語句和輔助處理程序,在循環之外是判斷終點的語句和進行剪枝的語句。判斷終點的語句自然是爲了停止遞歸,循環是爲了在搜索樹上進行橫向擴展即遍歷每一個狀態節點的所有直接子節點,而遞歸則是爲了縱向進行搜索達到DFS或者BFS。剪枝語句涉及到進行剪枝的條件或者說函數,這個函數涉及到搜索的效率,因爲他決定了是否繼續進行搜索,該狀態是否已經不是最優的了。
提高剪枝搜索效率的關鍵在於改良搜索路徑和條件函數。更加快速並且準確的判斷狀態進而判斷是否繼續進行搜索。
void CPrefixSorting::Search(int *pCakesArray, int step)
{
++searchCount;
if (LowerBound(pCakesArray) + step >= reversesCount)
{
return;
}
if (IsSorted(pCakesArray) && step < reversesCount)
{
reversesCount = step;
for (int i = 0; i < reversesCount; ++i)
{
cakesArrayReverse[i] = curCakesArrayReverse[i];
cout << curCakesArrayReverse[i] << " ";
}
cout << endl;
return;
}
for (int i = 1; i < cakesCount; ++i)
{
int *cakesArrayTmp = new int[cakesCount];
assert(NULL != cakesArrayTmp);
for (int j = 0; j < cakesCount; ++j)
{
cakesArrayTmp[j] = pCakesArray[j];
}
Reverse(cakesArrayTmp, 0, i);
curCakesArrayReverse[step] = i;
Search(cakesArrayTmp, step + 1);
if (cakesArrayTmp)
{
delete [] cakesArrayTmp;
}
}
}