【问题标题】:Coin change, dynamic programming, but coin value reduces after first use硬币变化,动态规划,但硬币价值在首次使用后减少
【发布时间】:2016-01-23 15:26:39
【问题描述】:

周围有很多硬币找零问题和答案,但我找不到这个,我想知道它是否甚至是硬币找零问题。

基本上我有一堆不同的硬币,每个硬币的数量是无限的。

所以说有成堆的各种面额。每个堆栈都是无限的。 (所以无限数量的 25c 硬币,无限数量的 2c 硬币等)。

但是,每叠硬币的顶部都有一枚特殊硬币,其价值大于(或等于)下面的硬币。如果不使用上面的硬币,我无法访问下面的硬币。

我要计算的是达到一定金额所需的最少硬币数量。

我认为这是可解决的动态规划,但我不确定如何将此限制添加到传统解决方案中。

我想知道是否应该在使用后将其删除并用普通硬币替换它,但我似乎无法推断这是否会破坏算法。

【问题讨论】:

  • 基本上就是找零钱DP。你应该一次处理一个硬币堆,稍微复杂一点的 DP 更新。
  • 我正在考虑计算从 0 到总和的每个值的最小硬币(就像传统解决方案一样)。为此,我通过遍历堆栈 L 的列表来检查每个堆栈。如果我发现总和是使用堆栈的特殊硬币完成的。我将用普通硬币(在列表 L 中)替换那个特殊硬币,它会永远保持这种状态。

标签: algorithm dynamic-programming knapsack-problem coin-change


【解决方案1】:

看起来像一个经典的动态规划问题,其中的挑战是正确选择状态。

通常,我们选择选择硬币的总和作为问题状态,选择硬币的数量作为状态值。过渡是我们可以采取的每一种可能的硬币。如果我们有 25c 和 5c 硬币,我们可以从值为 Count 的状态 Sum 移动到状态 Sum+25,count+1Sum+5,count+1

出于您的限制,状态应该增加关于哪些特殊(顶级)硬币被拿走的信息。因此,您为每叠硬币添加一点。然后,您只需要定义每个状态的可能转换。这很简单:如果设置了堆栈位,则意味着顶部硬币已经被拿走,您可以从该堆栈中添加一个非顶部硬币到状态总和,保持所有位相同。否则,您可以从该堆栈中取出顶部的硬币,将其值添加到状态总和,并设置相关位。

您从总和为 0 的状态开始,所有位清零且值为 0,然后构建从最低总和到目标的状态。

最后,您应该遍历所有可能的位和组合。将状态值与目标总和以及该位组合进行比较。选择最小值 - 这就是答案。

示例解决方案代码:

#Available coins: (top coin value, other coins value)
stacks = [(17,8),(5,3),(11,1),(6,4)]
#Target sum
target_value = 70

states = dict()
states[(0,0)] = (0,None,None)
#DP going from sum 0 to target sum, bottom up:
for value in xrange(0, target_value):
    #For each sum, consider every possible combination of bits
    for bits in xrange(0, 2 ** len(stacks)):
        #Can we get to this sum with this bits?
        if (value,bits) in states:
            count = states[(value,bits)][0]
            #Let's take coin from each stack
            for stack in xrange(0, len(stacks)):                
                stack_bit = (1 << stack)                
                if bits & stack_bit:
                    #Top coin already used, take second
                    cost = stacks[stack][1]
                    transition = (value + cost, bits)
                else:
                    #Top coin not yet used
                    cost = stacks[stack][0]
                    transition = (value + cost, bits | stack_bit)
                #If we can get a better solution for state with sum and bits
                #update it 
                if (not (transition in states)) or states[transition][0] > count + 1:
                    #First element is coins number
                    #Second is 'backtrack reference'
                    #Third is coin value for this step
                    states[transition] = (count+1, (value,bits), cost)

min_count = target_value + 1
min_state = None
#Find the best solution over all states with sum=target_value
for bits in xrange(0, 2 ** len(stacks)):
    if (target_value,bits) in states:
        count = states[(target_value,bits)][0]
        if count < min_count:
            min_count = count
            min_state = (target_value, bits)
collected_coins = []
state = min_state
if state == None:
    print "No solution"
else:
    #Follow backtrack references to get individual coins
    while state <> (0,0):
        collected_coins.append(states[state][2])
        state = states[state][1]
    print "Solution: %s" % list(reversed(collected_coins))

【讨论】:

  • 对不起,我以为我明白了,但实际上我没有。我不太明白你的最后一段,是否可以扩展倒数第二段和最后一段之间发生的事情?例如,试图用这个堆栈找到总和 10: 6(5) 3(2) 目前我的问题是我拿了一枚硬币 (6) 使总和 6 并标记该堆栈已被占用(堆栈变为 5)。总共使用了 10 个这 5 个中的两个。 Sum 6 没有用作解决方案的一部分。所以我显然在所有可能的位部分都错过了这个迭代?
  • 是的,感谢您的努力!这是有道理的——有额外的要求肯定会增加我一开始考虑的更多时间复杂度。
【解决方案2】:

以下解决方案基于两个事实 1) 所有可用面额的硬币数量无限

Algorithm:-

Let Given number be x

call the below method repeatedly until you reach your coin value, 
Each time of iteration pass the remaining value to be assigned with coins    and also the coin denomination

Method which will receive the number and the coin value
Parameters: x - coin to be allocated
            z - value of coin
if(x > z) {
   integer y = x/z * z
   if(y == x) {
      x is divisible by z and hence allocate with x/z number of coins of z value.
 }  
   if(Y != x) {
       x is not a multiple of z and hence allocate with x/z number of coins from z cents value and then for remaining amount repeat the same logic mentioned here by having the next greatest denomination of
  coin.

   }
 }
 else if(x < z) {
     return from this method;
 }
 else if(x == z) {
     x is divisible by z and hence allocate with x/z number of coins of z value    
 }


Example iteration:

Given number = 48c
Coins denomination: 25c, 10c, 5c, 2c, 1c

First Iteration:

Parameter x = 48c and z = 25c
return value would be a map of 1 coin of 25c, [25c , 1]
calculate the remaining amount 48c - 25c = 23c
23c is not equal to zero and not equal to 1 continue the loop.

Second Iteration:

Parameter x = 23c and z = 10c
return value would be a map of 2 coins of 10c, [10c, 2]
calculate the remaining amount 23c - 20c = 3c
3c is not equal to zero and not equal to 1 continue the loop.

Third Iteration:

Parameter x = 3c and z = 5c
No coins allocated
3c is not equal to zero and not equal to 1 continue the loop.

Fourth Iteration:

Parameter x = 3c and z = 2c
return value would be a map of 1 coin of 2c, [2c, 1]
Remaining amount 3c - 2c = 1c
Remaining amount Equals to 1 add an entry in map [1c, 1]


Final Map Entries:

[25c, 1]
[10c, 2]
[2c, 1]
[1c, 1]
[1c, 1]

【讨论】:

  • 贪心算法不适用于这个问题。反例是 x=29,coins={20, 14, 1}。最佳值为 3=2*14,1*1。贪心解是 10=1*20,9*1
猜你喜欢
  • 2023-03-20
  • 2018-02-25
  • 2012-11-24
  • 2011-02-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多