編程之美1.3 一摞烙餅的排序(Python)

問題:
     星期五的晚上,一幫同事在希格瑪大廈附近的“硬盤酒吧”多喝了幾杯。程序員多喝了幾杯之後談什麼呢?自然是算法問題。有個同事說:“我以前在餐館打工,顧客經常點非常多的烙餅。店裏的餅大小不一,我習慣在到達顧客飯桌前,把一摞餅按照大小次序擺好——小的在上面,大的在下面。由於我一隻手託着盤子,只好用另一隻手,一次抓住最上面的幾塊餅,把它們上下顛倒個個兒,反覆幾次之後,這摞烙餅就排好序了。我後來想,這實際上是個有趣的排序問題:假設有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. 如果沒有到達上界,創建它的子節點

     這樣看下來發現其實不需要存儲這棵樹,只需要一個棧就行了。每個節點存儲着之前的交換信息。差不多可以開始寫代碼了。Python中的List可以作爲棧使用,棧頂爲List的末尾,棧底爲起始位置,這個要記清楚。注意我們假設每一張餅大小都不相同。

     寫完了。上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()


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章