【问题标题】:Coin change DP solution to keep track of coins硬币找零DP方案跟踪硬币
【发布时间】:2013-12-09 17:41:57
【问题描述】:

尝试为一般的硬币找零问题编写 DP 解决方案,同时跟踪使用了哪些硬币。到目前为止,我一直在努力为我提供所需的最少硬币数量,但无法弄清楚如何获得使用了哪些硬币以及使用了多少次。如果使用硬币,我尝试使用值设置另一个表(布尔值),但这似乎无法正常工作。 有什么想法吗?

public static int minChange(int[] denom, int changeAmount) 
{   
    int m = denom.length;
    int n = changeAmount + 1;

    int[][] table = new int[m][n];
    boolean[][] used = new boolean[m][n];
    for (int j = 0; j < n; j++)
        table[m - 1][j] = j;
    for (int i = 0; i < m; i++)
        table[i][0] = 0;

    for (int i = m-2; i >= 0; i--) //i denotes denominationIndex
    {
        for (int j = 1; j < n; j++) //j denotes current Amount
        {
            if (denom[i] > j)
            {
                table[i][j] = table[i+1][j];
                //used[i][j] = false;
            }
            else
            {
                table[i][j] = Math.min(1 + table[i][j-denom[i]], table[i+1][j]);
                /*Trying table for used coins
                if (1 + table[i][j-denom[i]] < table[i+1][j]) 
                    used[i][j] = true;
                else
                    used[i][j] = false;
                */
            }
        }
    }
}

【问题讨论】:

  • 提问时,“无法正常工作” 通常是不够的。请解释会发生什么以及这与您的预期有何不同。
  • 当我说它不能正常工作时,我的意思是该表在不属于最低硬币答案的区域中给了我真实的信息。例如,如果我使用 4 个面额为 10、7、6、1 的硬币运行它,总共找零 14,则布尔表对 7 和 6 的结果为真,而对 7 的结果应该为真。
  • table[i][j] 指的是我用来存储动态编程期间使用的最小硬币数量的表格。程序运行后,它将指向所需的正确最小硬币数量。
  • @JavierGarrido 我的意思是,这个表中的 i 和 j 是什么意思?
  • i 表示 denom[] 中的位置,表示我们可以使用的硬币值。 j 表示我们需要进行的更改量

标签: java algorithm dynamic-programming coin-change


【解决方案1】:

试试这个解决方案,它只使用 O(M) 内存并且具有 O(N*M) 复杂度:

   public static int[] minChange(int[] denom, int changeAmount)
    {
        int n = denom.length;
        int[] count = new int[changeAmount + 1];
        int[] from = new int[changeAmount + 1];

        count[0] = 1;
        for (int i = 0 ; i < changeAmount; i++)
            if (count[i] > 0)
                for (int j = 0; j < n; j++)
                {
                    int p = i + denom[j];
                    if (p <= changeAmount)
                    {
                        if (count[p] == 0 || count[p] > count[i] + 1)
                        {
                            count[p] = count[i] + 1;
                            from[p] = j;
                        }
                    }
                }

        // No solutions:
        if (count[changeAmount] == 0)
            return null;

        // Build answer.
        int[] result = new int[count[changeAmount] - 1];
        int k = changeAmount;
        while (k > 0)
        {
            result[count[k] - 2] = denom[from[k]];
            k = k - denom[from[k]];
        }

        return result;
    }

【讨论】:

  • M 的复杂度是多少?
【解决方案2】:
public static int minimumNumberOfWays(int []deno, int amount){
    int dp[] = new int[amount+1];
    dp[0]=0;
    int []prevCoin = new int[amount+1];  
    for(int j=1;j<=amount;j++){
        dp[j]=Integer.MAX_VALUE;
        for(int i=0;i<deno.length;i++){
            if(deno[i]<=j && (1+dp[j-deno[i]] < dp[j]) ){               
                dp[j]=1+dp[j-deno[i]];
                prevCoin[j]=deno[i];
            }                   
        }
    }

    int result = dp[amount];
    List<Integer> coinsAdded = new ArrayList<Integer>();
    for(int i=amount;i>=1;){
        coinsAdded.add(prevCoin[i]);
        int j=i;
        i=amount-prevCoin[i];
        amount = amount - prevCoin[j];
    }
    Integer [] coins = coinsAdded.toArray(new Integer[coinsAdded.size()]);
    System.out.println( Arrays.toString(coins)); 

【讨论】:

    【解决方案3】:

    以下似乎在 Python 中有效:

    def g( A, n ) :
        #
        if A == [] or n == 0 or min(A) > n :
            return []
    
        #
        else :
    
            #
            min_len = float( "inf" )
            min_seq = None
    
            #
            for i, a in enumerate( A ) :
                if a <= n :
                    #
                    tmp = [ a ] + g( A[:i] + A[i+1:], n-a )
    
                    #
                    if len( tmp ) < min_len :
                        min_seq = tmp
                        min_len = len( min_seq )
            #
            return min_seq
    
    
    #
    for i in xrange(20) :
        #
        A = list( nr.randint( 1, 10, 5 ) )
        print A, g( A[:-1], A[-1] )
    

    【讨论】:

      【解决方案4】:

      您不需要 dp 的第一个维度。您可以使用只有一维的数组 - 所有已使用硬币的总和。然后你可以简单地为你的 dp 的每个状态存储你最后使用的硬币的索引。我的意思是这样的:

      int[] dp = new int[n];
      int[] usedCoin = new int[n];
      
      for (int i=0; i < n; ++i) {
          dp[i] = -1; // state not reached
          usedCoin[i] = -1;
      }
      
      dp[0] = 0; // initial state -- we can have sum 0 without any coins
      for (int coinId = 0; coinId < m; coinId++)
          for (int sum = 0; sum + denom[coinId] < n; sum++) {
              int newSum = sum + denom[coinId];
              if (dp[newSum] == -1 || dp[newSum] > 1 + dp[sum]) {
                  dp[newSum] = 1 + dp[sum];
                  usedCoin[newSum] = coinId;
              }
          }
      

      如果你想用最少的硬币找到具体的分解,你可以这样做:

      int sum = changeAmount;
      System.out.println("The used coins are:");
      while (usedCoin[sum] != -1) {
          System.out.print(denom[ usedCoin[sum] ].toString() + " ");
          sum -= denom[ usedCoin[sum] ];
      }
      

      【讨论】:

        【解决方案5】:

        使用以下伪代码重构解决方案:-

        solutionSet = []
        
        i = denom.length-1
        
        j = changeAmount
        
        While(i>=0) {
        
           if(1+table[i][j-denom[i]]<table[i+1][j]) {
                 solutionSet.add(denom[i])
                 j = j - denom[i]
             }
           i--
        }
        

        注意:这里不需要使用额外的内存,除了需要存储解决方案

        【讨论】:

          【解决方案6】:
          public class CoinChange {
          
              public static void main(String[] args) {
                  int[] coins = {1,2,10,5};       
                  int amt = 37;       
                  makeChange(coins, amt); 
              }
          
              public static void makeChange(int[] coins, int amount){
                  //Sorting the coins
                  Arrays.sort(coins);
          
                  int n = coins.length - 1;
          
          
                  HashMap<Integer, Integer> map = new HashMap<Integer, Integer>();
          
                  while(amount > 0)
                  {
                      if(coins[n] <= amount)
                      {
                          int val = 1;
                          if(map.containsKey(coins[n]))
                          {
                              val = map.get(coins[n]);
                              val = val+1;
                          }
          
                          map.put(coins[n], val);
          
                          amount = amount - coins[n];
          
                      }
                      else
                      {
                          n--;
                      }
          
                  }
          
                  for(Map.Entry<Integer, Integer> entry: map.entrySet()){
          
                      System.out.println(entry.getKey() +":" + entry.getValue());
                  }
              }
          }
          

          【讨论】:

          • 这是贪婪的做法,不是DP!贪婪并不总能找到最优解。
          【解决方案7】:
          public class CoinChange {
          /**
           * Get mini num of coins
           * 
           * @param coinValues
           *            coins
           * @param totalMoney
           *            total money
           * @return
           */
          public int coinNum(int[] coinValues, int totalMoney) {
              List<Integer> coins = new ArrayList<Integer>();
              coins.add(0);
              for (int i = 1; i <= totalMoney; i++) {
                  int coin = nearestCoin(i, coinValues);
                  int coinNum = coins.get(i - coin) + 1;
                  coins.add(coinNum);
              }
              return coins.get(totalMoney);
          }
          
          /**
           * Get the coin nearest to specified value.
           */
          private int nearestCoin(int value, int[] coinValues) {
              int res = 0;
              int nearest = Integer.MAX_VALUE;
              for (int coinValue : coinValues) {
                  if (coinValue <= value) {
                      int distance = value - coinValue;
                      if (distance < nearest) {
                          nearest = distance;
                          res = coinValue;
                      }
                  }
              }
              return res;
          }
          
          @Test
          public void test() throws Exception {
              int res = coinNum(new int[] { 1, 2, 3, 5, 11 }, 81);
              System.out.println(res);
          }
          

          }

          【讨论】:

            【解决方案8】:

            /* 硬币找零问题

            输入规范: 第一行预计金额 第二行期望硬币的数量 第三行包含按面额升序排列的硬币 假设硬币供应无限

            输出规格: 每个箱子首先显示最低面额的硬币,然后是次高面额的硬币。 案例由行分隔 如果无法形成金额,则打印 -1 注意:这不是 DP 解决方案 */

            #include<iostream>
            using namespace std;
            int *num,*coins,*maxC,n,amount,flag=0,stop=0;
            int sum()
            {
                int i=0,j;
                int sum=0;
                for(i=0;i<n;++i)
                    for(j=0;j<num[i];++j)
                        sum+=coins[i];
                return sum;
            }
            void print()
            {
                int i,j;
                for(i=0;i<n;++i)
                {
                    for(j=0;j<num[i];++j)
                        cout<<coins[i]<<" ";
                }
                cout<<endl;
            }
            void printNum()
            {
                int i;
                for(i=0;i<n;++i)
                    cout<<num[i]<<" ";
                cout<<endl;
            }
            void nextIter()
            {
                int i,j;
                int stat=0;
                //printNum();   //Remove the comment if you want to see the values of num array in every iteration
                for(i=0;i<n;++i)
                {
                    if(num[i]==0)
                        stat=1;
                    else
                    {
                        stat=0;
                        break;
                    }
                }
                if(stat)
                {
                    stop=1;
                    return ;
                }
                for(i=n-1;i>=0;--i)
                {
                    int dec=0;
                    if(num[i]==0)
                    {
                        dec=1;
                        num[i]=maxC[i];
                    }
                    else
                    {
                        --num[i];
                        return ;
                    }
                }
            }
            int find()
            {
                while(!stop)
                {
                    if(amount==sum())
                    {
                        flag=1;
                        print();
                    }
                    nextIter();
                }
            }
            int main()
            {
                int i;
                cout<<"\nEnter amount:";
                cin>>amount;
                cout<<"\nEnter number of coins:";
                cin>>n;
                coins=new int[n];
                maxC=new int[n];//contains maximum number of each denomination required
                num=new int[n];//contains current number of each denomination required
                for(i=0;i<n;++i)
                {
                    cin>>coins[i];
                    num[i]=maxC[i]=amount/coins[i];
                }
                find();
                if(!flag)
                    cout<<"-1";
                cout<<endl;
                system("pause");
                return 0;
            }
            

            【讨论】:

              【解决方案9】:

              使用动态编程的解决方案 ::

              public int coinchange2(ArrayList<Integer> A, int B) {
                  int[] dp = new int[B+1];
              
                  dp[0]=1;
              
                  for(int i=0;i<A.size();i++){
                      for(int j=A.get(i);j<=B;j++)
                      {
                          dp[j]=(dp[j]+dp[j-A.get(i)])%1000007;
                      }
                  }
                  return dp[B];
              }
              

              【讨论】:

              • 虽然这段代码 sn-p 可以解决问题,但including an explanation 确实有助于提高帖子的质量。请记住,您是在为将来的读者回答问题,而这些人可能不知道您提出代码建议的原因。
              猜你喜欢
              • 2012-08-05
              • 2015-09-02
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2023-03-02
              • 2020-05-09
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多