90.奇妙货币交易问题 | 豆包MarsCode AI刷题

122 阅读3分钟

问题描述

小R住在一个名为 X 国的国家,这里的货币非常特殊,面值为 V0,V1,V2,...,VnV0,V1,V2,...,V**n,并且 nn 可以无限大。该国的交易规则也很特别:在一次交易中,双方只能对每种面值的货币使用不超过两次。

例如,小R想买一件价格为 198 的物品,货币的基数 V=10V=10 时,小R可以使用 2 张 100 的纸币,卖家则找回 2 张 1的纸币。由于这个奇怪的规则,很多 X 国人都无法快速判断某个物品是否可以用这种方式交易成功,他们常常会请聪明的你来帮助。

你能帮他们判断一下,是否能按照规则用给定的货币面值 V来完成价格为 W的交易吗?

可以使用 动态规划 来优化解决这个问题。动态规划的核心思想是:通过状态转移来减少重复计算

以下是优化思路:

状态定义

  1. 用一个布尔数组 dp 表示:金额 jjj 是否可以通过合法支付方式(每种面值最多使用 2 次)构造出来。
  2. 初始化:dp[0] = True(金额 0 一定可以构造)。
  3. 转移公式:对于每个面值 ViV^iVi,尝试使用 0、1 或 2 次更新 dp

转移过程

  • 对于每个面值

    V^i:

    • 从金额 W开始向下更新 dp,避免重复计算。
    • 使用该面值 0、1、2 次尝试更新 dp

最终判断

  • 如果支付金额 P≥W满足 dp[P],并且找零金额 P−W也能通过合法组合构造,则输出 YES

以下是优化后的实现:

优化后的代码测试结果如下:

  • 样例 1:输入 V=10,W=9V = 10, W = 9V=10,W=9,输出:YES
  • 样例 2:输入 V=200,W=40199V = 200, W = 40199V=200,W=40199,输出:YES
  • 样例 3:输入 V=108,W=50V = 108, W = 50V=108,W=50,输出:NO

dp的一种,稍微一变就感觉想不出来了,关键在于从后往前让一个货币面值最多用两次,而且最后要检验找零的合理性。

def solution(V, W):
    if V == 1:
        return "YES"
   # 计算可能用到的最大金额(买方最多支付两倍W)
    max_exponent = 0
    while V**max_exponent <= W * 2:
        max_exponent += 1
​
    # 所有可能的面值
    denominations = [V**i for i in range(max_exponent)]
​
    # 动态规划数组,表示是否能组合出金额 j
    dp = [False] * (W * 2 + 1)
    dp[0] = True  # 金额 0 可以用 0 张货币构造
​
    # 更新 dp 表
    for d in denominations:
        for j in range(W * 2, d - 1, -1):  # 从大到小遍历
            if dp[j - d]:
                dp[j] = True  # 使用 1 张 d
            if j >= 2 * d and dp[j - 2 * d]:
                dp[j] = True  # 使用 2 张 d
​
    # 检查支付和找零的合法性
    for P in range(W, W * 2 + 1):  # 支付金额至少是 W
        if dp[P] and dp[P - W]:
            return "YES"
​
    return "NO"