1. 程式人生 > 實用技巧 >動態規劃-分蛋糕V2

動態規劃-分蛋糕V2

分蛋糕:
有一塊矩形大蛋糕,長和寬分別是整數w?、h。現要將其切成m塊小蛋糕,
每個小蛋糕都必須是矩形、且長和寬均為整數。切蛋糕時,每次切一塊蛋糕,
將其分成兩個矩形蛋糕。請計算:最後得到的m塊小蛋糕中,最大的那塊蛋糕的面積下限。
假設w= 4,?h= 4,?m= 4,則下面的切法可使得其中最大蛋糕塊的面積最小。
假設w= 4,?h= 4,?m= 3,則下面的切法會使得其中最大蛋糕塊的面積最小:
Input
共有多行,每行表示一個測試案例。每行是三個用空格分開的整數w, h, m ,
其中1 ≤ w, h, m ≤ 20 , m ≤ wh. 當 w = h = m = 0 時不需要處理,
表示輸入結束。

Output
每個測試案例的結果佔一行,輸出一個整數,表示最大蛋糕塊的面積下限。
Sample Input
4 4 4
4 4 3
0 0 0
Sample Output
4
6

"""
# 4 4 3
"""
[
[[-1, -1, -1], [-1, -1, -1], [-1, -1, -1], [-1, -1, -1], [-1, -1, -1]],
[[-1, -1, -1], [1, inf, inf], [2, 1, inf], [3, 2, 1], [4, 2, 2]],
[[-1, -1, -1], [2, 1, inf], [4, 2, 2], [6, 3, 2], [8, 4, 3]],
[[-1, -1, -1], [3, 2, 1], [6, 3, 2], [9, 6, 3], [12, 6, 4]],

[[-1, -1, -1], [4, 2, 2], [8, 4, 3], [12, 6, 4], [16, 8, 【6】]]
]
【6】位置,表示w=4,h=4,d=2的值
思路:遞推就是就是後續的值是由前面計算過的值求出來的。
只要理解其中的一種情況,如:minBiggestArea[4][4][2]
求w=4,h=4,d=2的最大蛋糕的最小值
對蛋糕切1刀下去後,假設第1刀豎切,位置在w=3的位置,
那麼就分成了2塊蛋糕,那麼用剩下的刀數分別求這兩塊蛋糕的最大蛋糕的最小值
假設左邊最大蛋糕的最小值v1,右邊最大蛋糕的最小值v2,
max(v1,v2),兩者中的最大值,既是minBiggestArea[4][4][2]第一刀切在w=3位置

最大蛋糕的最小值。
總共可以切2刀,第1刀已經切了,那麼左右蛋糕最多隻能切1刀
其中一種情況:遍歷左邊切0刀,右邊只能切1刀
v1=minBiggestArea[3][4][0],v2=minBiggestArea[1][4][1]
其中一種情況:遍歷左邊切1刀,右邊只能切0刀
v1=minBiggestArea[3][4][1],v2=minBiggestArea[1][4][0]
minBiggestArea[4][4][2] = max(v1,v2)
因為不同的切法,max(v1,v2)能求出許多不同的值,題目要求最小的值,因此
需要從那麼多個值當中找出最小的值存到minBiggestArea[4][4][2]

python程式碼:
# minBigestArea三維列表,0:寬,1:高,2:共切幾刀,在w,h的長方形切m刀最大蛋糕的面積最小值
minBiggestArea = []
INF = float("inf")


# w-寬度 h-高度 d-刀數
def BiggestCakeMinSize(w, h, d):
    global minBiggestArea
    # 寬度
    for i in range(1, w+1):
        # 高度
        for j in range(1, h+1):
            # 列舉共可以切的刀數
            for k in range(1, d+1):
                if k+1 > i*j:
                    minBiggestArea[i][j][k] = INF
                else:
                    # 豎切
                    SV = INF
                    sv, sh = 0, 0
                    # 列舉豎著切寬度的位置
                    for ii in range(1, i):
                        for kk in range(0, k):
                            sv = max(minBiggestArea[ii][j][kk], minBiggestArea[i-ii][j][k-1-kk])
                            SV = min(SV, sv)
                    # 橫切
                    SH = INF
                    # 列舉橫著切高度的位置
                    for jj in range(1, j):
                        for kk in range(0, k):
                            sh = max(minBiggestArea[i][jj][kk], minBiggestArea[i][j-jj][k-1-kk])
                            SH = min(SH, sh)
                    minBiggestArea[i][j][k] = min(SV, SH)
    return minBiggestArea[w][h][d]


def main():
    global minBiggestArea
    while True:
        w, h, m = map(int, input().split())
        if w == 0 and h == 0 and m == 0:
            break
        # 構建1個三維列表,因為列表的序號從0開始計算,為了計算方便w、h多增加1,
        # 列表中w、h位置0的值沒有意義,不儲存資料,僅為計算方便
        # 三維列表從1開始儲存,初始化為-1,如果計算過,就儲存值下來,避免重複計算,提高效率
        # m這個位置儲存的是刀數,要分成m塊,需要m-1刀,比如m=3,只需要2刀,所以只需要生成0、1、2
        # 因此m的位置不需要增加1
        minBiggestArea = [[[-1 for i in range(m)] for i in range(h+1)] for i in range(w+1)]
        # 先計算如果一刀都不切的情況下的值,最大蛋糕最小值即寬*高
        for i in range(1, w+1):
            for j in range(1, h+1):
                minBiggestArea[i][j][0] = i*j
        """
        [
            [[-1, -1, -1], [-1, -1, -1], [-1, -1, -1], [-1, -1, -1], [-1, -1, -1]],
            [[-1, -1, -1], [1, -1, -1], [2, -1, -1], [3, -1, -1], [4, -1, -1]],
            [[-1, -1, -1], [2, -1, -1], [4, -1, -1], [6, -1, -1], [8, -1, -1]],
            [[-1, -1, -1], [3, -1, -1], [6, -1, -1], [9, -1, -1], [12, -1, -1]],
            [[-1, -1, -1], [4, -1, -1], [8, -1, -1], [12, -1, -1], [16, -1, -1]]
        ]
        """
        # m塊蛋糕需要切m-1刀
        optimalValue = BiggestCakeMinSize(w, h, m-1)
        print("最大蛋糕塊的面積下限為:%d" % optimalValue)

    return 0


if __name__ == '__main__':
    main()