2017年12月26日 星期二

b029: 福袋!福袋!

b029: 福袋!福袋!

正誼百貨公司的週年慶到了,該公司推出福袋的促銷活動,除了百萬名車等大獎外,一般福袋則是由5樣商品組成,這5樣商品的價格分別是2元、5元、10元、20元、25元,每包福袋中的每種商品可以放很多個,也可以完全不放,請問以這5種商品要湊出 N 元的福袋,總共有幾種不同的組合。
輸入說明:
輸入一個正整數 N (2<=N<=1000),代表福袋的價值。
輸出說明:
請輸出以這5種商品湊出 N 元的福袋總共有幾種不同的組合。
範例輸入:
輸入1:
10

輸入2:
20
範例輸出 :
輸出1:
3

輸出2:
7

硬幣面值組合問題

問題描述

  假設我們有8種不同面值的硬幣{1,2,5,10,20,50,100,200},用這些硬幣組合夠成一個給定的數值n。例如n=200,那麼一種可能的組合方式為 200 = 3 * 1 + 1*2 + 1*5 + 2*20 + 1 * 50 + 1 * 100. 問總過有多少種可能的組合方式? (這道題目來自著名程式設計網站ProjectEuler, 點擊這裡查看原題目) 類似的題目還有:
  [華為面試題] 1分2分5分的硬幣三種,組合成1角,共有多少種組合
  [創新工廠筆試題] 有1分,2分,5分,10分四種硬幣,每種硬幣數量無限,給定n分錢,有多少中組合可以組成n分錢

問題分析

  給定一個數值sum,假設我們有m種不同類型的硬幣{V1, V2, ..., Vm},如果要組合成sum,那麼我們有
sum = x1 * V1 + x2 * V2 + ... + xm * Vm 
求所有可能的組合數,就是求滿足前面等值的係數{x1, x2, ..., xm}的所有可能個數。
  [思路1] 當然我們可以採用暴力枚舉,各個係數可能的取值無非是x1 = {0, 1, ..., sum / V1}, x2 = {0, 1, ..., sum/ V2}等等。這對於硬幣種類數較小的題目還是可以應付的,比如華為和創新工廠的題目,但是複雜度也很高O(sum/V1 * sum/V2 * sum/V3 * ...)
  [思路2] 從上面的分析中我們也可以這麼考慮,我們希望用m種硬幣構成sum,根據最後一個硬幣Vm的係數的取值為無非有這麼幾種情況,xm分別取{0, 1, 2, ..., sum/Vm},換句話說,上面分析中的等式和下面的幾個等式的聯合是等價的。
sum = x1 * V1 + x2 * V2 + ... + 0 * Vm
sum = x1 * V1 + x2 * V2 + ... + 1 * Vm
sum = x1 * V1 + x2 * V2 + ... + 2 * Vm
...
sum = x1 * V1 + x2 * V2 + ... + K * Vm  

  其中K是該xm能取的最大數值K = sum / Vm。可是這又有什麼用呢?不要急,我們先進行如下變數的定義:
dp[i][sum] = 用前i種硬幣構成sum 的所有組合數。
  那麼題目的問題實際上就是求dp[m][sum],即用前m種硬幣(所有硬幣)構成sum的所有組合數。在上面的聯合等式中:當xn=0時,有多少種組合呢? 實際上就是前i-1種硬幣組合sum,有dp[i-1][sum]種! xn = 1 時呢,有多少種組合? 實際上是用前i-1種硬幣組合成(sum - Vm)的組合數,有dp[i-1][sum -Vm]種; xn =2呢, dp[i-1][sum - 2 * Vm]種,等等。所有的這些情況加起來就是我們的dp[i][sum]。所以:
dp[i][sum] = dp[i-1][sum - 0*Vm] + dp[i-1][sum - 1*Vm]
+ dp[i-1][sum - 2*Vm] + ... + dp[i-1][sum - K*Vm]; 其中K = sum / Vm
  換一種更抽象的數學描述就是:
 

  通過此公式,我們可以看到問題被一步步縮小,那麼初始情況是什麼呢?如果sum=0,那麼無論有前多少種來組合0,只有一種可能,就是各個係數都等於0;
dp[i][0] = 1   // i = 0, 1, 2, ... , m
  如果我們用二位元陣列表示dp[i][sum], 我們發現第i行的值全部依賴與i-1行的值,所以我們可以逐行求解該陣列。如果前0種硬幣要組成sum,我們規定為dp[0][sum] = 0. 


#====================================================================
# Dynamic Programming Python implementation of Coin Change problem
def count(S, m, n):
    # We need n+1 rows as the table is consturcted in bottom up
    # manner using the base case 0 value case (n = 0)
    table = [[0 for x in range(m)] for x in range(n+1)]

    # Fill the enteries for 0 value case (n = 0)
    for i in range(m):
        table[0][i] = 1

    # Fill rest of the table enteries in bottom up manner
    for i in range(1, n+1):
        for j in range(m):
            # Count of solutions including S[j]
            x = table[i - S[j]][j] if i-S[j] >= 0 else 0

            # Count of solutions excluding S[j]
            y = table[i][j-1] if j >= 1 else 0

            # total count
            table[i][j] = x + y

    return table[n][m-1]

# Driver program to test above function
arr = [2, 5, 10,20 ,25]
m = len(arr)
print("福袋則是由5樣商品組成,這5樣商品的價格分別是2元、5元、10元、20元、25元,")
print("每包福袋中的每種商品可以放很多個,")
print("也可以完全不放,請問以這5種商品要湊出 N 元的福袋,總共有幾種不同的組合。")
n = int(input("輸入一個正整數 N (2<=N<=1000),代表福袋的價值 : "))
print("這5種商品湊出",n,"元的福袋總共有",count(arr, m, n),"種不同的組合。")


# This code is contributed by Bhavya Jain





====================== RESTART: F:/Python_APSC/b029.py ======================
福袋則是由5樣商品組成,這5樣商品的價格分別是2元、5元、10元、20元、25元,
每包福袋中的每種商品可以放很多個,
也可以完全不放,請問以這5種商品要湊出 N 元的福袋,總共有幾種不同的組合。
輸入一個正整數 N (2<=N<=1000),代表福袋的價值 : 10
這5種商品湊出 10 元的福袋總共有 3 種不同的組合。
>>> 
====================== RESTART: F:/Python_APSC/b029.py ======================
福袋則是由5樣商品組成,這5樣商品的價格分別是2元、5元、10元、20元、25元,
每包福袋中的每種商品可以放很多個,
也可以完全不放,請問以這5種商品要湊出 N 元的福袋,總共有幾種不同的組合。
輸入一個正整數 N (2<=N<=1000),代表福袋的價值 : 20
這5種商品湊出 20 元的福袋總共有 7 種不同的組合。
>>> 
====================== RESTART: F:/Python_APSC/b029.py ======================
福袋則是由5樣商品組成,這5樣商品的價格分別是2元、5元、10元、20元、25元,
每包福袋中的每種商品可以放很多個,
也可以完全不放,請問以這5種商品要湊出 N 元的福袋,總共有幾種不同的組合。
輸入一個正整數 N (2<=N<=1000),代表福袋的價值 : 100
這5種商品湊出 100 元的福袋總共有 273 種不同的組合。
>>> 



b028: 忙碌的超商店員

b028: 忙碌的超商店員

還記得有這樣的經驗嗎?你去買一個10元的東西,你付了1張100元的鈔票,店員卻找你9個10元,甚至有找你5個10元4個5元和10個1元的情況。為了避免這樣的情況,某超市在每一間門市都準備了足夠的零錢,並且要求店員找給顧客的零錢個數一定要是最少的。已知這個國家使用的貨幣有1元、5元、10元、12元、16元、20元等6種硬幣,你能幫他解決這個問題嗎?
輸入說明:
輸入一個正整數 N (1<=N<=100),代表要找的錢。
輸出說明:
請輸出找給顧客 N 元時,最少的零錢個數。
範例輸入:
輸入1:
13

輸入2:
29
範例輸出 :
輸出1:
2

輸出2:
3
"""
http://uceylove55.blogspot.tw/2015/02/algorithm-greedy-algorithm-part-2.html
演算法 Algorithm ─ Greedy Algorithm PART 2
貪婪演算法 Greedy Algorithm

○ 換零錢遊戲

例:現有 71個 1元,幣值分別為 29元、22元、5元、1元,請用最少的零錢個數兌換零錢。

※  貪婪演算法:
    ○ 局部解 :     29元    22元    5元    1元
1. 換 29元           2個
2. 換 22元                   0個
3. 換 5元                            2個
4. 換 1元                                   3個
------------------------------------------------------------------------------
      總共換了      2+2+3=  7 枚硬幣

※  動態演算法:    
    ○ 最佳解 :    29元     22元     5元     1元
1. 換 29元           0個
2. 換 22元                   3個
3. 換 5元                            1個
4. 換 1元                                    0個
--------------------------------------------------------------------------------
      總共換了      3+1=  4 枚硬幣


由此可知:

● 貪婪演算法不一定是最佳解,但效率高
● 動態演算法可以求出最佳解,但效率略差 
● 要依問題的特性判斷採用何種演算法,依此換零錢例子,應該採用動態演算法(Dynamic Programming)
"""

def dynamicCoinChange( T, L ):

Opt = [0 for i in range(0, L+1)]

n = len(T)
for i in range(1, L+1):
smallest = float("inf")
for j in range(0, n):
if (T[j] <= i):
smallest = min(smallest, Opt[i - T[j]]) 
Opt[i] = 1 + smallest 
return Opt[L]


# Coin change situations
problems = [
# [[1, 5, 10, 20, 50, 100, 200],  10000000],
[[1, 3, 4],  20],
[[1, 2, 3],  9],
[[1, 2, 3],  10],
[[1, 5], 13923],
[[7, 22, 71, 231], 753],
[[3, 5, 12], 25],
[[800], 800],
[[2], 50000]
]


# Test Loop
for T, L in problems:
S = dynamicCoinChange(T, L)
print ("dynamicCoinChange(T, L)")
print ("T =", T)
print ("L =", L)
print ("Answer =", S)

T=[1,5,10,12,16,20]
print("1元、5元、10元、12元、16元、20元等6種硬幣")
L=int(input("輸入一個正整數 N (1<=N<=100),代表要找的錢 > "))
S=dynamicCoinChange( T, L )

print("最少的零錢個數",S)


           


====================== RESTART: F:/Python_APSC/b028.py ======================
dynamicCoinChange(T, L)
T = [1, 3, 4]
L = 20
Answer = 5
dynamicCoinChange(T, L)
T = [1, 2, 3]
L = 9
Answer = 3
dynamicCoinChange(T, L)
T = [1, 2, 3]
L = 10
Answer = 4
dynamicCoinChange(T, L)
T = [1, 5]
L = 13923
Answer = 2787
dynamicCoinChange(T, L)
T = [7, 22, 71, 231]
L = 753
Answer = 7
dynamicCoinChange(T, L)
T = [3, 5, 12]
L = 25
Answer = 4
dynamicCoinChange(T, L)
T = [800]
L = 800
Answer = 1
dynamicCoinChange(T, L)
T = [2]
L = 50000
Answer = 25000
1元、5元、10元、12元、16元、20元等6種硬幣
輸入一個正整數 N (1<=N<=100),代表要找的錢 > 29
最少的零錢個數 3
>>> 

b027: 小綠人的城堡

b027: 小綠人的城堡


森林裡住了一群小綠人,因為受到藍色巨人的攻擊,所以他們決定要建造一座城堡。不過由於樹是他們生活環境必須的,所以他們只會選擇沒有樹的地方,而且城堡一定要蓋成正方形,請你幫他們找出可以蓋最大城堡的地方有多大。
輸入說明:
第一行有兩個正整數 H、W (1<=H、W<=100),代表這塊土地的大小,接下來有 H 行,每行有 W 個 0 或 1 的數字,1 代表樹,0 則是可以蓋城堡的地方。
輸出說明:
請你找出上述土地中,全部由 0 組合成的正方形區域的最大面積有多少。
範例輸入:
3 4
0 0 1 0
1 0 0 0
0 0 0 1
範例輸出 :
4

題目:

Given a 2D binary matrix filled with 0's and 1's, find the largest square containing all 1's and return its area.
For example, given the following matrix:
1 0 1 0 0
1 0 1 1 1
1 1 1 1 1
1 0 0 1 0
Return 4.

動態規劃 

當我們判斷以某個點為正方形右下角時最大的正方形時,那它的上方,左方和左上方三個點也一定是某個正方形的右下角,否則該點為右下角的正方形最大就是它自己了。這是定性的判斷,那具體的最大正方形邊長呢?我們知道,該點為右下角的正方形的最大邊長,最多比它的上方,左方和左上方為右下角的正方形的邊長多1,最好的情況是是它的上方,左方和左上方為右下角的正方形的大小都一樣的,這樣加上該點就可以構成一個更大的正方形。但如果它的上方,左方和左上方為右下角的正方形的大小不一樣,合起來就會缺了某個角落,這時候只能取那三個正方形中最小的正方形的邊長加1了。假設dpi表示以i,j為右下角的正方形的最大邊長,則有

dp[i][j] = min(dp[i-1][j-1], dp[i-1][j], dp[i][j-1]) + 1
當然,如果這個點在原矩陣中本身就是0的話,那dpi肯定就是0了。
public class Solution {
    public int maximalSquare(char[][] matrix) {
        if(matrix.length == 0) return 0;
        int m = matrix.length, n = matrix[0].length;
        int max = 0;
        int[][] dp = new int[m][n];
        // 第一列賦值
        for(int i = 0; i < m; i++){
            dp[i][0] = matrix[i][0] - '0';
            max = Math.max(max, dp[i][0]);
        }
        // 第一行賦值
        for(int i = 0; i < n; i++){
            dp[0][i] = matrix[0][i] - '0';
            max = Math.max(max, dp[0][i]);
        }
        // 遞推
        for(int i = 1; i < m; i++){
            for(int j = 1; j < n; j++){
                dp[i][j] = matrix[i][j] == '1' ? Math.min(dp[i-1][j-1], 
                        Math.min(dp[i-1][j], dp[i][j-1])) + 1 : 0;
                max = Math.max(max, dp[i][j]);
            }
        }
        return max * max;
    }
}

Question - post

Given a 2D binary matrix filled with 0's and 1's, find the largest rectangle containing all ones and return its area.

Example 1:

["10100",
 "10111",
 "11111",
 "10010"
Return 6

Example 2:

["101111",
 "011111",
 "011011",
 "001111",
 "101100"]
Return 8

Example 3:

["1111",
 "1111",
 "1111"
Return 12

Solution - code

Given a navigation that goes through the elements from the left-top one to the right-bottom one, we need to know at a specific element, what is the certain information that defines the maximal rectangle from the previous left or top elements. In terms of this mentor model, the problem is essentially a Dynamic Programming (wiki) problem. So the point is ...
  • To define the smallest subproblem so that we could break the problem into smallest subproblems.
  • Use memorization to store the results of the solved subproblems.
The smallest subproblem is that: For every row of the array, it is a "Largest Rectangle in Histogram" problem.

Step 1 - Subproblem "Largest Rectangle in Histogram"

...
For every element, we go through the elements behind. The code is like,
def maximalRectangleInHistogram(self, histogram):
    """
    Calculate the maximum rectangle area in terms of a given histogram.
    (Ver.1)
    :param histogram: List[int]
    :return:  int
    """
    maxArea = 0
    size = len(histogram)
    # For every bar in the histogram, we go through the previous bars.
    for i in range(size):
        # Take current height as the minimum height.
        minH = histogram[i]
        for j in reversed(range(i)):
            # Return if it meets zero.
            if histogram[j] == 0:
                break
            # Find the minimum height at the moment.
            minH = min(minH, histogram[j])
            # Calculate the area at the moment.
            area = minH * (i - j + 1)
            # Find out the maximum area.
            maxArea = max(maxArea, area)
    return maxArea
Can we do this better? Sure! We could try to use stack coming with certain rules to improve the back navigation. So the problem becomes when to push and when to pop. I still don't figure out the way to improve the previous version and it's the code from the internet:
def maximalRectangleInHistogram(self, histogram):
    """
    Calculate the maximum rectangle area in terms of a given histogram.
    (Ver.2)
    :param histogram: List[int]
    :return:  int
    """
    # Stack for storing the index.
    posStack = []
    i = 0
    maxArea = 0
    while i < len(histogram):
        if len(posStack) == 0 or histogram[i] > histogram[posStack[-1]]:
            # Advance the index when either the stack is empty or the
            # current height is greater than the top one of the stack.
            posStack.append(i)
            i += 1
        else:
            curr = posStack.pop()
            width = i if len(posStack) == 0\
                else i - posStack[-1] - 1
            maxArea = max(maxArea, width * histogram[curr])
    # Clean the stack.
    while posStack:
        curr = posStack.pop()
        width = i if len(posStack) == 0\
            else len(histogram) - posStack[-1] - 1
        maxArea = max(maxArea, width * histogram[curr])
    return maxArea

Step 2 - Break The Problem Into Subproblems

...
For every row of the array, it is a "Largest Rectangle in Histogram" problem and the code is like,
class Solution(object):
    def maximalRectangle(self, matrix):
        """
        :type matrix: List[List[str]]
        :rtype: int
        """
        if not matrix:
            return 0
        maxArea = 0
        maxRow = len(matrix)
        maxCol = len(matrix[0])
        # For every row in the given 2D matrix, it is a "Largest Rectangle in
        # Histogram" problem, which is the subproblem.
        lookupTable = [0 for _ in range(maxCol)]
        for row in range(maxRow):
            for col in range(maxCol):
                # If it is "1"
                if int(matrix[row][col]) > 0:
                    # Accumulate the column if if's 1's.
                    lookupTable[col] += int(matrix[row][col])
                else:
                    # Clean the column if it's 0's.
                    lookupTable[col] = 0
            # Calculate the maximum area.
            maxArea = max(maxArea,
                          self.maximalRectangleInHistogram(lookupTable))
        return maxArea
"""
題目:
Given a 2D binary matrix filled with 0's and 1's,
find the largest square containing all 1's and return its area.

For example, given the following matrix:
1 0 1 0 0
1 0 1 1 1
1 1 1 1 1
1 0 0 1 0
Return 4.

動態規劃

當我們判斷以某個點為正方形右下角時最大的正方形時,
那它的上方,左方和左上方三個點也一定是某個正方形的右下角
,否則該點為右下角的正方形最大就是它自己了。
這是定性的判斷,那具體的最大正方形邊長呢?
我們知道,該點為右下角的正方形的最大邊長,
最多比它的上方,左方和左上方為右下角的正方形的邊長多1
,最好的情況是是它的上方,左方和左上方為右下角的正方形的大小都一樣的
,這樣加上該點就可以構成一個更大的正方形。
但如果它的上方,左方和左上方為右下角的正方形的大小不一樣
,合起來就會缺了某個角落,這時候只能取那三個正方形中
最小的正方形的邊長加1了。
假設dpi表示以i,j為右下角的正方形的最大邊長,則有

 dp[i][j] = min(dp[i-1][j-1], dp[i-1][j], dp[i][j-1]) + 1

當然,如果這個點在原矩陣中本身就是0的話,那dpi肯定就是0了。

"""
def printMaxSubSquare(M):
    R = len(M) # no. of rows in M[][]
    C = len(M[0]) # no. of columns in M[][]

    S = [[0 for k in range(C)] for l in range(R)]
    # here we have set the first row and column of S[][]

    # Construct other entries
    for i in range(1, R):
        for j in range(1, C):
            if (M[i][j] == 1):
                S[i][j] = min(S[i][j-1], S[i-1][j],
                            S[i-1][j-1]) + 1
            else:
                S[i][j] = 0
     
    # Find the maximum entry and
    # indices of maximum entry in S[][]
    max_of_s = S[0][0]
    max_i = 0
    max_j = 0
    for i in range(R):
        for j in range(C):
            if (max_of_s < S[i][j]):
                max_of_s = S[i][j]
                max_i = i
                max_j = j

    print("Maximum size sub-matrix is: ")
    for i in range(max_i, max_i - max_of_s, -1):
        for j in range(max_j, max_j - max_of_s, -1):
            print (M[i][j], end = " ")
        print("")
        global max_width
        max_width= max_i

# Driver Program
max_width=0
M = [[0, 1, 1, 0, 1],
    [1, 1, 0, 1, 0],
    [0, 1, 1, 1, 0],
    [1, 1, 1, 1, 0],
    [1, 1, 1, 1, 1],
    [0, 0, 0, 0, 0]]

printMaxSubSquare(M)
# This code is contributed by Soumen Ghosh


M = [[1,0,1,1,1,1],
     [0,1,1,1,1,1],
     [0,1,1,0,1,1],
     [0,0,1,1,1,1],
     [1,0,1,1,0,0]]

printMaxSubSquare(M)
# This code is contributed by Soumen Ghosh



====================== RESTART: F:/Python_APSC/b027.py ======================
Maximum size sub-matrix is: 
1 1 1 
1 1 1 
1 1 1 
Maximum size sub-matrix is: 
1 1 
1 1 
>>> 





  清除項目編譯快取 執行 flutter clean 重新取得套件依賴 執行 flutter pub get 完整重新編譯並執行 flutter run 1. 新增專案套件 (pubspec.yaml) 在 Flutter 專案根目錄下的 pubspec.yaml 中,新增 ...