問題:
星期五的晚上,一幫同事在希格瑪大廈附近的「硬碟酒吧」多喝了幾杯。程式設計師多喝了幾杯之後談什麼呢?自然是演算法問題。有個同事說:「我以前在餐館打工,顧客經常點非常多的烙餅。店裡的餅大小不一,我習慣在到達顧客飯桌前,把一摞餅按照大小次序擺好——小的在上面,大的在下面。由於我乙隻手托著盤子,只好用另乙隻手,一次抓住最上面的幾塊餅,把它們上下顛倒個個兒,反覆幾次之後,這摞烙餅就排好序了。我後來想,這實際上是個有趣的排序問題:假設有n塊大小不一的烙餅,那最少要翻幾次,才能達到最後大小有序的結果呢?」
你能否寫出乙個程式,對於n塊大小不一的烙餅,輸出最優化的翻餅過程呢?
分析: 題目要求的是最優化方案,而不是僅僅將其排好序。一種簡單的排序方法是,首先將最上面到最大的餅翻轉,這樣最大的餅到了最上面,再整體翻轉,這樣最大的餅就排好序了。按照這樣的方法依次去拍n-1大的,n-2大的……經過2n-2次翻轉,完成排序。也就是說,這個問題的最優解的乙個上界是2n-2,我們要尋找最優化的解。
尋找最優解的很直接的想法就是搜尋。搜尋的缺點是相對其他方法比較消耗記憶體和時間,但是比較通用,很多問題也只適合用搜尋解,其他方法不適合用。動態規劃相比搜尋會有更好的效率。考慮如果使用動態規劃,動態規劃需要劃分問題具有優化子結構,以及子問題重疊性。但在這個問題裡似乎不太好劃分子問題。
搜尋的步驟就是把全部的交換方式組織成一棵樹,對這顆樹進行遍歷操作,到達葉子節點表示找到了一種可行解,我們要尋找的是所有可行解中的最優情況。廣度優先遍歷需要儲存整個樹在記憶體中,非常消耗空間,因此使用深度優先遍歷。對深搜的優化可以使用分支定界,對不可能的解空間減少不必要的搜尋。樹的建立的過程就是遍歷的過程,由於需要進行回退,因此不能在原陣列上進行排序,而是需要額外的空間進行儲存,因此考慮在每個節點儲存當前整個陣列。樹的建立是動態的,一旦確定是需要拋棄的,就直接刪除節省空間。
總結一下:
1. 設有n張餅,交換方式上界為2n-2,那麼每個節點有n-1個子節點
2. 實際上每個節點有n-2個子節點,因為有乙個節點必然不成立:不可能和上一層的交換方式相同,那樣就換回去了
3. 但是這個節點的位置還得留著,給個空指標即可
4. 每個節點儲存的內容有:當前陣列,層數,全部的交換步驟
5. 根節點層數標為0,因為我們要的實際上是交換次數
6. 需要乙個全域性變數,儲存當前已經找到的可行解的上界
遍歷和剪枝的過程:
1. 對於每個節點,考察它是否已經排好序
2. 如果已經排好序,比較它和當前的上界的大小,如果更小,那麼修改上界為當前值。返回
3. 如果沒有排好序,考察它是否已經達到上界
4. 如果已經達到上界,返回
5. 如果沒有到達上界,建立它的子節點
這樣看下來發現其實不需要儲存這棵樹,只需要乙個棧就行了。每個節點儲存著之前的交換資訊。差不多可以開始寫**了。jetbrains的pycharm社群版不久之前宣布開源,正好試用一下。python中的list可以作為棧使用,棧頂為list的末尾,棧底為起始位置,這個要記清楚。注意我們假設每一張餅大小都不相同。
寫完了,感覺pycharm相當不錯啊,除錯比pydev強多了。上python**
#!/usr/bin/python
__author__ = 'dell'
class node:
cakes =
currentlevel = 0
steps =
def inputcakes(cakes):
global n
global upperbound
n = int(raw_input('n:'))
upperbound = 2 * n - 2
for i in range(0, n):
cakes.append(int(raw_input(str(i) + ':')))
def init():
global stack
node = node()
inputcakes(node.cakes)
#node.cakes = swap(node.cakes, 1)
stack.append(node)
def swap(cakes, position):
newcakes = cakes[:]
for i in range(0, position / 2 + 1):
temp = newcakes[i]
newcakes[i] = newcakes[position - i]
newcakes[position - i] = temp
return newcakes
def issorted(cakes):
for i in range(0, len(cakes) - 1):
if cakes[i] > cakes[i+1]:
return false
return true
def dfs():
global stack
global upperbound
global n
while len(stack) != 0:
node = stack[-1]
stack.pop()
if issorted(node.cakes):
print 'found:', node.steps
if node.currentlevel < upperbound:
upperbound = node.currentlevel
else:
if node.currentlevel < upperbound:
for i in range(1, n):
if len(node.steps) == 0 or (len(node.steps) > 0 and i != node.steps[-1]):
childnode = node()
childnode.cakes = swap(node.cakes, i)
childnode.currentlevel = node.currentlevel + 1
childnode.steps = node.steps[:]
childnode.steps.append(i)
stack.append(childnode)
n = 0
upperbound = 0
stack =
if __name__ == '__main__':
init()
dfs()
程式設計之美1 3 一摞烙餅的排序
程式設計之美 讀書筆記 1.3 一摞烙餅的排序 問題 星期五的晚上,一幫同事在希格瑪大廈附近的 硬碟酒吧 多喝了幾杯。程式設計師多喝了幾杯之後談什麼呢?自然是演算法問題。有個同事說 我以前在餐館打工,顧客經常點非常多的烙餅。店裡的餅大小不一,我習慣在到達顧客飯桌前,把一摞餅按照大小次序擺好 小的在上...
程式設計之美 一摞烙餅排序
問題 星期五的晚上,一幫同事在希格瑪大廈附近的 硬碟酒吧 多喝了幾杯。程式設計師多喝了幾杯之後談什麼呢?自然是演算法問題。有個同事說 我以前在餐館打工,顧客經常點非常多的烙餅。店裡的餅大小不一,我習慣在到達顧客飯桌前,把一摞餅按照大小次序擺好 小的在上面,大的在下面。由於我乙隻手托著盤子,只好用另乙...
程式設計之美 一摞烙餅的排序
問題描述 星期五的晚上,一幫同事在希格瑪大廈附近的 硬碟酒吧 多喝了幾杯。程式設計師多喝了幾杯之後談什麼呢?自然是演算法問題。有個同事說 我以前在餐館打工,顧客經常點非常多的烙餅。店裡的餅大小不一,我習慣在到達顧客飯桌前,把一摞餅按照大小次序擺好 小的在上面,大的在下面。由於我乙隻手托著盤子,只好用...