日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

【编程之美】一摞烙饼的排序

發(fā)布時間:2023/12/8 编程问答 40 豆豆
生活随笔 收集整理的這篇文章主要介紹了 【编程之美】一摞烙饼的排序 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

一,問題:????????????

??????? 星期五的晚上,一幫同事在希格瑪大廈附近的“硬盤酒吧”多喝了幾杯。程序員多喝了幾杯之后談什么呢?自然是算法問題。有個同事說:“我以前在餐館打工,顧客經(jīng)常點非常多的烙餅。店里的餅大小不一,我習(xí)慣在到達(dá)顧客飯桌前,把一摞餅按照大小次序擺好——小的在上面,大的在下面。由于我一只手托著盤子,只好用另一只手,一次抓住最上面的幾塊餅,把它們上下顛倒個個兒,反復(fù)幾次之后,這摞烙餅就排好序了。我后來想,這實際上是個有趣的排序問題:假設(shè)有n塊大小不一的烙餅,那最少要翻幾次,才能達(dá)到最后大小有序的結(jié)果呢?”

??????? 你能否寫出一個程序,對于n塊大小不一的烙餅,輸出最優(yōu)化的翻餅過程呢?(參考flyingherts的專欄)

?

二,分析:


??????? n個烙餅經(jīng)過翻轉(zhuǎn)后的所有狀態(tài)可組成一棵樹。尋找翻轉(zhuǎn)最少次數(shù),相當(dāng)于在樹中搜索層次最低的某個節(jié)點

??????? 由于每層的節(jié)點數(shù)呈幾何數(shù)量級增長,在n較大時,使用廣度優(yōu)先遍歷樹,可能沒有足夠的內(nèi)存來保存中間結(jié)果(考慮到每層的兩個節(jié)點,可以通過旋轉(zhuǎn),移位等操作互相轉(zhuǎn)換,也許每層的狀態(tài)可以用一個函數(shù)來生成,這時可以采用廣度優(yōu)先方法),因而采用深度優(yōu)先。但這棵樹是無限深的,必須限定搜索的深度(即最少翻轉(zhuǎn)次數(shù)的上限值),當(dāng)深度達(dá)到該值時不再繼續(xù)往下搜索。最少翻轉(zhuǎn)次數(shù),必然小等于任何一種翻轉(zhuǎn)方案所需的翻轉(zhuǎn)次數(shù),因而只要構(gòu)造出一種方案,取其翻轉(zhuǎn)次數(shù)即可做為其初始值。最簡單的翻轉(zhuǎn)方案就是:對最大的未就位的烙餅,將其翻轉(zhuǎn),再找到最終結(jié)果中其所在的位置,翻轉(zhuǎn)一次使其就位。因此,對編號在n-1和2之間的烙餅,最多翻轉(zhuǎn)了2*(n-2)次,剩下0和1號烙餅最多翻轉(zhuǎn)1次,因而最少翻轉(zhuǎn)次數(shù)的上限值是:2*(n-2)+1=2*n-3(從網(wǎng)上可搜索到對該上限值最新研究結(jié)果:上限值為18/11*n),當(dāng)然,最好還是直接計算出采用這種方案的翻轉(zhuǎn)次數(shù)做為初始值。

?

?

三,減少遍歷次數(shù):

?

???? 1 )減小“最少翻轉(zhuǎn)次數(shù)上限值”的初始值,采用前面提到的翻轉(zhuǎn)方案,取其翻轉(zhuǎn)次數(shù)為初始值。對書中的例子{3,2,1,6,5,4,9,8,7,0},初始值可以取10。

?

???? 2 ) 避免出現(xiàn)已處理過的狀態(tài)一定會減少遍歷嗎?答案是否定的,深度優(yōu)先遍歷,必須遍歷完一個子樹,才能遍歷下一個子樹,如果一個解在某層比較靠后位置,若不允許處理已出現(xiàn)過的狀態(tài)時,可能要經(jīng)過很多次搜索,才能找到這個解,但允許處理已出現(xiàn)過的狀態(tài)時,可能會很快找到這個解,并減小“最少翻轉(zhuǎn)次數(shù)的上限值”,使更多的分支能被剪掉,反而能減少遍歷的節(jié)點數(shù)。比如說,兩個子樹A、B,搜索子樹A,100次后可得到一個對應(yīng)翻轉(zhuǎn)次數(shù)為20的解,搜索子樹B,20次后可得到翻轉(zhuǎn)次數(shù)為10的解,不允許處理已出現(xiàn)過的狀態(tài),就會花100次遍歷完子樹A后,才開始遍歷B,但允許翻轉(zhuǎn)回上一次狀態(tài),搜索會在A、B間交叉進(jìn)行,就可能只要70次找到子樹B的那個解(翻轉(zhuǎn)次數(shù)為10+2=12),此時,翻轉(zhuǎn)次數(shù)上限值比較小,可忽略更多不必要的搜索。以書中的{3,2,1,6,5,4,9,8,7,0}為例,按程序(1.3_pancake_1.cpp),不允許翻轉(zhuǎn)回上次狀態(tài)時需搜索195次,而允許翻轉(zhuǎn)回上次狀態(tài)時只要搜索116次。

?

????? 3) 如果最后的幾個烙餅已經(jīng)就位,只須考慮前面的幾個烙餅。對狀態(tài)(0,1,3,4,2,5,6),編號為5和6的烙餅已經(jīng)就位,只須考慮前5個烙餅,即狀態(tài)(0,1,3,4,2)。如果一個最優(yōu)解,從某次翻轉(zhuǎn)開始移動了一個已經(jīng)就位的烙餅,且該烙餅后的所有烙餅都已經(jīng)就位,那么對這個解法,從這次翻轉(zhuǎn)開始得到的一系列狀態(tài),從中移除這個烙餅,可得到一系列新的狀態(tài)。必然可以設(shè)計出一個新的解法對應(yīng)這系列新的狀態(tài),而該解法所用的翻轉(zhuǎn)次數(shù)不會比原來的多。

?

?????? 4 )估計每個狀態(tài)還需要翻轉(zhuǎn)的最少次數(shù)(即下限值),加上當(dāng)前的深度,如果大等于上限值,就無需繼續(xù)遍歷。這個下限值可以這樣確定:從最后一個位置開始,往前找到第一個與最終結(jié)果位置不同的烙餅編號(也就是說排除最后幾個已經(jīng)就位的烙餅),從該位置到第一個位置,計算相鄰的烙餅的編號不連續(xù)的次數(shù),再加上1。每次翻轉(zhuǎn)最多只能使不連續(xù)的次數(shù)減少1,但很多人會忽略掉這個情況:最大的烙餅沒有就位時,必然需要一次翻轉(zhuǎn)使其就位,而這次翻轉(zhuǎn)卻不改變不連續(xù)次數(shù)。(可以在最后面增加一個更大的烙餅,使這次翻轉(zhuǎn)可以改變不連續(xù)數(shù)。)如:對狀態(tài)(0,1,3,4,2,5,6)等同于狀態(tài)(0,1,3,4,2),由于1、3和4、2不連續(xù),因而下限值為2+1=3。下限值也可以這樣確定:在最后面增加一個比所有烙餅都大的已經(jīng)就位的烙餅,然后再計算不連續(xù)數(shù)。如:(0,1,3,4,2),可以看作(0,1,3,4,2,5),1和3 、4和2 、2和5這三個不連續(xù),下限值為3。

?

????? 5)多數(shù)情況下,翻轉(zhuǎn)次數(shù)的上限值越大,搜索次數(shù)就越多。可以采用貪心算法,通過調(diào)整每次所有可能翻轉(zhuǎn)的優(yōu)先順序,盡快找到一個解,從而減少搜索次數(shù)。比如,優(yōu)先搜索使“下限值”減少的翻轉(zhuǎn),其次是使“下限值”不變的翻轉(zhuǎn),最后才搜索使“下限值”增加的翻轉(zhuǎn)。對“下限值”不變的翻轉(zhuǎn),還可以根據(jù)其下次的翻轉(zhuǎn)對“下限值”的影響,再重新排序。由于進(jìn)行了優(yōu)先排序,翻轉(zhuǎn)回上一次狀態(tài)能減少搜索次數(shù)的可能性得到進(jìn)一步降低。

?

?????? 6 )其它剪枝方法:

????????????? 假設(shè)進(jìn)行第m次翻轉(zhuǎn)時,“上限值”為min_swap。

???????????? 如果翻轉(zhuǎn)某個位置的烙餅?zāi)苁顾欣语灳臀?#xff08;即翻轉(zhuǎn)次數(shù)剛好為m),則翻轉(zhuǎn)其它位置的烙餅,能得到的最少翻轉(zhuǎn)次數(shù)必然大等m,因而這些位置都可以不搜索。

???????????? 如果在某個位置的翻轉(zhuǎn)后,“下限值”為k,并且 k+m>=min_swap,則對所有的使新“下限值”kk大等于k的翻轉(zhuǎn),都有 kk+m>=min_swap,因而都可以不搜索。該剪枝方法是對上面的“調(diào)整翻轉(zhuǎn)優(yōu)先順序”的進(jìn)一步補(bǔ)充。

?

???????????? 另外,翻轉(zhuǎn)某個烙餅時,只有兩個烙餅位置的改變才對“下限值”有影響,因而可以記錄每個狀態(tài)的“下限值”,進(jìn)行下一次翻轉(zhuǎn)時,只須通過幾次比較,就可以確定新狀態(tài)的“下限值”。(判斷不連續(xù)次數(shù)時,最好寫成 -1<=x && x<=1, 而不是x==1 || x==-1。對于 int x; a<=x && x<=b,編譯器可以將其優(yōu)化為 unsigned (x-a) <= b-a。)

?

結(jié)果:

對書上的例子{3,2,1,6,5,4,9,8,7,0}:

?

翻轉(zhuǎn)回上次狀態(tài)

搜索函數(shù)被調(diào)用次數(shù)

翻轉(zhuǎn)函數(shù)被調(diào)用次數(shù)

1.3_pancake_2

不允許

29

66

1.3_pancake_2

允許

33

74

1.3_pancake_1

不允許

195

398

1.3_pancake_1

允許

116

240

這個例子比較特殊,代碼1.3_pancake_2.cpp(與1.3_pancake_1.cpp的最主要區(qū)別在于,增加了對翻轉(zhuǎn)優(yōu)先順序的判斷,?代碼下載),在不允許翻轉(zhuǎn)回上次狀態(tài)且取min_swap的初始值為2*10-2=18時,調(diào)用搜索函數(shù)29次,翻轉(zhuǎn)函數(shù)56次)。

?

搜索順序?qū)Y(jié)果影響很大,如果將1.3_pancake_2.cpp第152行:

for (int pos=1, last_swap=cake_swap[step++]; pos<size; ++pos){

這一行改為:

for (int pos=size-1, last_swap=cake_swap[step++]; pos>=1; --pos){

僅僅調(diào)整了搜索順序,調(diào)用搜索函數(shù)次數(shù)由29次降到11次(對應(yīng)的翻轉(zhuǎn)方法:9,6,9,6,9,6),求第1個烙餅數(shù)到第10個烙餅數(shù),所用的總時間也由原來的38秒降到21秒。)


四,源碼及分析


#include <iostream> #include <cassert> #include <cstdio> class laobing { private:int *m_CakeArray; // 烙餅信息數(shù)組int m_nCakeCnt; // 烙餅個數(shù)int m_nMaxSwap; // 最多交換次數(shù)。根據(jù)前面的推斷,這里最多為// m_nCakeCnt * 2 int *m_SwapArray; // 交換結(jié)果數(shù)組int *m_ReverseCakeArray; // 當(dāng)前翻轉(zhuǎn)烙餅信息數(shù)組int *m_ReverseCakeArraySwap; // 當(dāng)前翻轉(zhuǎn)烙餅交換結(jié)果數(shù)組int m_nSearch; // 當(dāng)前搜索次數(shù)信息public:laobing() {m_nCakeCnt = 0;m_nMaxSwap = 0;}~laobing(){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;}//// 計算烙餅翻轉(zhuǎn)信息// @param// pCakeArray 存儲烙餅索引數(shù)組// nCakeCnt 烙餅個數(shù)//void Run(int* pCakeArray, int nCakeCnt){Init(pCakeArray, nCakeCnt);m_nSearch = 0;Search(0);}void mOutput(int* CakeArray, int nCakeCnt,int *m_SwapArray,int m_nMaxSwap){int t; for(int i = 0; i < m_nMaxSwap; i++)//swap times{for(int j1 = 0,j2=m_SwapArray[i]; j1<j2; j1++,j2--) //reverse array{t = CakeArray[j1]; CakeArray[j1] = CakeArray[j2];CakeArray[j2] = t;}for(int k=0;k<nCakeCnt;++k)printf("%d ", CakeArray[k]);printf("\n");}}void Output()// 輸出烙餅具體翻轉(zhuǎn)的次數(shù){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);mOutput(m_CakeArray, m_nCakeCnt,m_SwapArray,m_nMaxSwap);//輸出交換過程}private://// 初始化數(shù)組信息// @param// pCakeArray 存儲烙餅索引數(shù)組// nCakeCnt 烙餅個數(shù)//void Init(int* pCakeArray, int nCakeCnt){assert(pCakeArray != NULL);assert(nCakeCnt > 0);m_nCakeCnt = nCakeCnt;// 初始化烙餅數(shù)組m_CakeArray = new int[m_nCakeCnt]; assert(m_CakeArray != NULL);for(int i = 0; i < m_nCakeCnt; i++){m_CakeArray[i] = pCakeArray[i];}// 設(shè)置最多交換次數(shù)信息m_nMaxSwap = UpBound(m_nCakeCnt);// 初始化交換結(jié)果數(shù)組 m_SwapArray = new int[m_nMaxSwap + 1];assert(m_SwapArray != NULL);// 初始化中間交換結(jié)果信息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)// 尋找當(dāng)前翻轉(zhuǎn)的上界{return nCakeCnt*2;}int LowerBound(int* pCakeArray, int nCakeCnt) // 尋找當(dāng)前翻轉(zhuǎn)的下界{int t, ret = 0;// 根據(jù)當(dāng)前數(shù)組的排序信息情況來判斷最少需要交換多少次for(int i = 1; i < nCakeCnt; i++){// 判斷位置相鄰的兩個烙餅,是否為尺寸排序上相鄰的t = pCakeArray[i] - pCakeArray[i-1];if((t == 1) || (t == -1)){} else{ret++;}}return ret;}// 排序的主函數(shù)void Search(int step){int i, nEstimate;m_nSearch++;// 估算這次搜索所需要的最小交換次數(shù)nEstimate = LowerBound(m_ReverseCakeArray, m_nCakeCnt);if(step + nEstimate > m_nMaxSwap)return;// 如果已經(jīng)排好序,即翻轉(zhuǎn)完成,輸出結(jié)果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;}// 遞歸進(jìn)行翻轉(zhuǎn)for(i = 1; i < m_nCakeCnt; i++){Revert(0, i);//反轉(zhuǎn)m_ReverseCakeArraySwap[step] = i; //第一步 反轉(zhuǎn)的哪一個Search(step + 1);Revert(0, i);}}//// true : 已經(jīng)排好序// false : 未排序//bool IsSorted(int* pCakeArray, int nCakeCnt){for(int i = 1; i < nCakeCnt; i++){if(pCakeArray[i-1] > pCakeArray[i]){return false;}}return true;}//// 翻轉(zhuǎn)烙餅信息// void Revert(int nBegin, int nEnd){assert(nEnd > nBegin);int i, j, t;// 翻轉(zhuǎn)烙餅信息for(i = nBegin, j = nEnd; i < j; i++, j--){t = m_ReverseCakeArray[i]; m_ReverseCakeArray[i] = m_ReverseCakeArray[j];m_ReverseCakeArray[j] = t;}}};int main() {laobing ll;//這里ll 不可以加括號laobing *l=new laobing();int aa[10]={ 3,2,1,6,5,4,9,8,7,0};l->Run(aa,10);l->Output();ll.Run(aa,10);return 0; }
輸出結(jié)果:

4 8 6 8 4 9
|Search Times| : 172126
Total Swap times = 6
5 6 1 2 3 4 9 8 7 0
7 8 9 4 3 2 1 6 5 0
1 2 3 4 9 8 7 6 5 0
5 6 7 8 9 4 3 2 1 0
9 8 7 6 5 4 3 2 1 0
0 1 2 3 4 5 6 7 8 9

五,優(yōu)化:

在網(wǎng)上下了《編程之美》“第6刷”的源代碼,結(jié)果在編譯時存在以下問題:

1) Assert 應(yīng)該是 assert

2) m_arrSwap 未被定義,應(yīng)該改為m_SwapArray

3 )Init函數(shù)兩個for循環(huán),后一個沒定義變量i,應(yīng)該將i 改為 int i

另外,每運行一次Run函數(shù),就會調(diào)用Init函數(shù),就會申請新的內(nèi)存,但卻沒有釋放原來的內(nèi)存,會造成內(nèi)存泄漏。if(step + nEstimate > m_nMaxSwap) 這句還會造成后面對m_ReverseCakeArraySwap數(shù)組的越界訪問,使程序不能正常運行。

?

書上程序的低效主要是由于進(jìn)行剪枝判斷時,沒有考慮好邊界條件,可進(jìn)行如下修改:

1 ) if(step + nEstimate > m_nMaxSwap)? >改為 >=

2 ) 判斷下界時,如果最大的烙餅不在最后一個位置,則要多翻轉(zhuǎn)一次,因而在LowerBound函數(shù)return ret; 前插入行:

??????????????? if (pCakeArray[nCakeCnt-1] != nCakeCnt-1)

??????????????????????????????????????? ret++;

3 ) n個烙餅,翻轉(zhuǎn)最大的n-2烙餅最多需要2*(n-2)次,剩下的2個最多1次,因而上限值為2*n-3,因此,m_nMaxSwap初始值可以取2*n-3+1=2*n-2,這樣每步與m_nMaxSwap的判斷就可以取大等于號。

4 )采用書上提到的確定“上限值”的方法,直接構(gòu)建一個初始解,取其翻轉(zhuǎn)次數(shù)為m_nMaxSwap的初始值。

?

???????? 1和2任改一處,都能使搜索次數(shù)從172126降到兩萬多,兩處都改,搜索次數(shù)降到3475。若再改動第3處,搜索次數(shù)降到2989;若采用4的方法(此時初始值為10),搜索次數(shù)可降到1045


六,思考

? ? ? ? ? 書中P22頁提到動態(tài)規(guī)劃,但最后卻給出了解決最優(yōu)化問題普遍適用但效率可能是最差的遞歸方法。這不禁讓人疑惑:這也不美啊!?如果我們能證明該問題滿足動態(tài)規(guī)劃或貪心算法的使用條件,解決問題的時間復(fù)雜度將會降到多項式時間甚至N^2。但書中提到動態(tài)規(guī)劃卻最終沒有使用,又沒有講明原因,我覺得是一種疏失(應(yīng)該不算錯誤)。那我們就來想一下為什么沒有動態(tài)規(guī)劃或貪心算法的原因。

? ? ? ? ?我們知道動態(tài)規(guī)劃方法是一種自底向上的獲取問題最優(yōu)解的方法,它采用子問題的最優(yōu)解來構(gòu)造全局最優(yōu)解。利用動態(tài)規(guī)劃求解的問題需要滿足兩個條件:即(1)最優(yōu)子結(jié)構(gòu) (2)子結(jié)構(gòu)具有重疊性。條件(1)使我們可以利用子問題的最優(yōu)解來構(gòu)造全局最優(yōu)解,而條件(2)是我們在計算過程中可以利用子結(jié)構(gòu)的重疊性來減少運算次數(shù)。此外,《算法導(dǎo)論》上還以有向圖的無權(quán)最短路徑和無權(quán)最長路徑為例提出條件(3)子問題必須獨立。
??
? ? ? ? ?首先我們假定烙餅問題存在優(yōu)化子結(jié)構(gòu)。假如我們有N個烙餅,把他們以其半徑由小到大進(jìn)行編號。優(yōu)化子結(jié)構(gòu)告訴我們對于i個烙餅,我們只需要先排列前(i-1)個,然后再將第i個歸位;或先排列第2到i個,最后將第一個歸位;又或是找到一個位置k[i<=k<j]像矩陣乘法加括號那樣,使得我們先排列前k個,再排列后j-k個,最后再將二者合并,以找到一個最佳翻轉(zhuǎn)策略等等...
??
? ? ? ? ?根據(jù)動態(tài)規(guī)劃算法的計算過程,我們需要一個N*N矩陣M,其中M[i][j]表示將編號i至編號j的烙餅排序所需要的翻轉(zhuǎn)次數(shù)。但我們真的能從M[0][0..j-1]和M[1][j+1],或與M[i][j]同行同列的值來計算M[i][j]嗎?如果能,我們就能獲得多項式時間的算法。??


? ? ? ? 我們來看書中給出的例子:(頂端)3,2,1,6,5,4,9,8,7,0(底端),我們最終的目標(biāo)是計算M[0][9]。
這里我們以計算M[0][4]為例,計算的矩陣我已經(jīng)在下面給出:??
? 0 1? 2? 3? 4? 5? 6? 7? 8? 9
? ------------------------
0|0 1 (1){1}[?]
1|? 0? 1 (1){1}??
2|???? 0? 1 (1)?
3|??????? 0? 0
4|?????????? 0
? ------------------?
??
? ? ? ? 實際上如果我們要向?qū)?-4號烙餅(注意:烙餅編號也等同于其半徑)排為正序(中間有其他烙餅也沒關(guān)系),按照程序給出的結(jié)果, 我們需要進(jìn)行3次翻轉(zhuǎn),分別為[2,5,9](即分別翻轉(zhuǎn)隊列中第二(從零開始)、五、九個烙餅,這里的數(shù)字不是烙餅的編號):??
? [1]? [2]? [3]? 6?? 5? [4]? 9? 8? 7? [0]
? [4]?? 5??? 6? [3] [2] [1]? 9? 8? 7? [0]
? [0]?? 7??? 8?? 9? [1] [2] [3] 6? 5? [4]
??
? ? ? ? ?我們知道,該矩陣中每一個數(shù)的背后都隱含著一個烙餅的排列,例如M[0][4]就應(yīng)該對應(yīng)0,7,8,9,1,2,3,6,5,4
? 所以,每一個M[i][j]的選取都蘊含著其子排列的順序的變化。
? ? ? ? 計算M[i][j]的時候,我們需要計算i-j號餅的全部劃分(不包括全部為1的劃分)所能構(gòu)成的翻轉(zhuǎn)結(jié)構(gòu),并取其翻轉(zhuǎn) 次數(shù)最少的哪一個最為M[i][j]的最終值。例如,我們在計算M[0][4]的時候,需要查看:
??
?? /**先將0和1-4號分別排序,最后將二者合并為有序所需要的翻轉(zhuǎn)次數(shù)*/
?? M[0][0],M[1][4]?
???
?? /** 同上 */
?? M[0][1],M[2][4]
???
?? /** 同上 */
?? M[0][2],M[3][4]
???
?? /** 同上 */
?? M[0][3],M[4][4]
???
?? /* 先將0、1、2、3-4號分別排序,最后將4者合并為有序所需要的翻轉(zhuǎn)次數(shù).
??? * 注意這里又包含將4個分組再次進(jìn)行劃分的問題!?
??? */
?? M[0][0],M[1][1],M[2][2],M[3][4]
?? .....//中間略
?? M[0][3],M[4][4]
??
? ? ?如果再加上運算過程中我們可以淘汰超過最大反轉(zhuǎn)次數(shù)的方案(剪枝?),我們完成全部的運算所經(jīng)歷的運算過程的時間復(fù)雜度已經(jīng)不是多項式時間的,而是和先前所說的遞歸方法已沒什么兩樣。

???? 造成這種現(xiàn)象的原因是:某個子問題的最優(yōu)解不一定是整體的最優(yōu)解,所以我們在處理整個問題的時候,需要遍歷所有可能的子問題,并計算它到整體問題所消耗的代價,才能最終作出有利于整體問題的選擇。

? ? ? 所以我們一開始的假設(shè),即烙餅問題有優(yōu)化子結(jié)構(gòu)的假設(shè)是錯誤的。因此我們不能用動態(tài)規(guī)劃,同理也不能用貪心算法。

??
? ? ? ?但說到每一步的“選擇”問題,我記得算法導(dǎo)論上有一個叫做“A*”的算法,它的思想是在進(jìn)行每一步選擇的時候都“推算”最終可能需要的代價,并選擇當(dāng)前代價最小的分支進(jìn)行遍歷。這個“推算”的結(jié)果可能不會是最終的代價,而只是作為分支選擇的依據(jù)。如果誰有興趣就做一下吧 :-)

??


總結(jié)

以上是生活随笔為你收集整理的【编程之美】一摞烙饼的排序的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。