10

尝试为一般的硬币找零问题编写 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

9 回答 9

6

试试这个解决方案,它只使用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;
    }
于 2013-11-25T08:02:50.657 回答
3
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)); 
于 2015-04-21T13:29:31.440 回答
0

以下似乎适用于 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] )
于 2013-11-25T06:31:52.127 回答
0

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

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--
}

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

于 2013-11-25T12:01:40.407 回答
0

您不需要 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] ];
}
于 2013-11-25T07:58:15.857 回答
0
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());
        }
    }
}
于 2015-05-14T13:58:12.370 回答
0

/* 硬币找零问题

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

输出规格:每个案例首先显示最低面额的硬币,然后是次高面额的硬币。案例以行分隔 如果无法形成金额 -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;
}
于 2017-08-05T17:27:05.073 回答
0
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);
}

}

于 2015-12-30T10:37:33.090 回答
-1

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

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];
}
于 2019-02-09T11:32:33.170 回答