# 动态规划问题
# 1. 斐波那契数列
# 2. 凑零钱

# --1-- 斐波那契数列,不是标准动态规划类问题,但是DP数组的使用思想是一样的

def _fib1(n):
    if n == 0:
        return 0
    if n == 1 or n == 2:
        return 1
    return _fib1(n-1) + _fib1(n-2)

def _fib_inter(n):
    a, b = 1, 1
    for _ in range(n-1):
        a, b = b, a + b
    return a

def fib_db(n):
    if n == 1 or n == 2:
        return 1
    if n == 0:
        return 0
    dp = [0]*(n+1)
    dp[1] = dp[2] = 1
    for i in range(3, n + 1):
        dp[i] = dp[i - 1] + dp[i - 2]
    return dp[n]


# --2-- 凑零钱,给定n种不同币值的银币,面额是c1...cn, 总金额amount,问最少可以使用几个硬币

# 递归解法
def coin_change_recur(coins, amount):
    record = {}
    def recur(n):
        if n in record:
            return record[n]
        if n < 0:
            return -1
        if n == 0:
            return 0
        res = float('inf')
        for coin in coins:
            subproblem = recur(n - coin)
            if subproblem == -1:
                continue
            res = min(res, 1 + subproblem)
        record[n] = res if res > -1 else -1
        return record[n]

    return recur(amount)

# dp解法
def coin_change_dp(coins, amount):
    # 创建一个数组 dp,dp[i] 表示组成金额 i 所需的最少硬币数
    # 初始化 dp 数组,设置一个较大的值代表无法构成金额
    dp = [float('inf')] * (amount + 1)
    dp[0] = 0  # 组成金额 0 不需要硬币,硬币数为 0

    # 遍历每个硬币
    for coin in coins:
        for i in range(1, amount + 1):
            dp[i] = min(dp[i], dp[i - coin] + 1)

    # 如果 dp[amount] 仍然是 inf,说明无法组成该金额,返回 -1
    return dp[amount] if dp[amount] != float('inf') else -1