Coin Change DP Algorithm Print All Combinations

2019-09-12 05:19发布

问题:

The classic coin change problem is well described here: http://www.algorithmist.com/index.php/Coin_Change

Here I want to not only know how many combinations there are, but also print out all of them. I'm using the same DP algorithm in that link in my implementation but instead of recording how many combinations in the DP table for DP[i][j] = count, I store the combinations in the table. So I'm using a 3D vector for this DP table.

I tried to improve my implementation noticing that when looking up the table, only information from last row is needed, so I don't really need to always store the entire table.

However my improved DP solution still seems quite slow, so I'm wondering if there's some problem in my implementation below or there can be more optimization. Thanks!

You can run the code directly:

#include <iostream>
#include <stdlib.h>
#include <iomanip>
#include <cmath>
#include <vector>
#include <algorithm>
using namespace std;

int main(int argc, const char * argv[]) {       

    int total = 10; //total amount
    //available coin values, always include 0 coin value
    vector<int> values = {0, 5, 2, 1}; 
    sort(values.begin(), values.end()); //I want smaller coins used first in the result 

    vector<vector<vector<int>>> empty(total+1); //just for clearing purpose
    vector<vector<vector<int>>> lastRow(total+1);
    vector<vector<vector<int>>> curRow(total+1);


    for(int i=0; i<values.size(); i++) {


        for(int curSum=0; curSum<=total; curSum++){
            if(curSum==0) {
                //there's one combination using no coins               
                curRow[curSum].push_back(vector<int> {}); 

            }else if(i==0) {
                //zero combination because can't use coin with value zero

            }else if(values[i]>curSum){
                //can't use current coin cause it's too big, 
                //so total combination for current sum is the same without using it
                curRow[curSum] = lastRow[curSum];

            }else{
                //not using current coin
                curRow[curSum] = lastRow[curSum];
                vector<vector<int>> useCurCoin = curRow[curSum-values[i]];

                //using current coin
                for(int k=0; k<useCurCoin.size(); k++){

                    useCurCoin[k].push_back(values[i]);
                    curRow[curSum].push_back(useCurCoin[k]);
                }               
            }    
        }        

        lastRow = curRow;
        curRow = empty;
    } 

    cout<<"Total number of combinations: "<<lastRow.back().size()<<endl;
    for (int i=0; i<lastRow.back().size(); i++) {
        for (int j=0; j<lastRow.back()[i].size(); j++) {
            if(j!=0)
                cout<<" ";
            cout<<lastRow.back()[i][j];
        }
        cout<<endl;
    }
    return 0;
}

回答1:

It seems that you copy too many vectors: at least the last else can be rewritten as

// not using current coin
curRow[curSum] = lastRow[curSum];
const vector<vector<int>>& useCurCoin = curRow[curSum - values[i]]; // one less copy here

// using current coin
for(int k = 0; k != useCurCoin.size(); k++){
    curRow[curSum].push_back(useCurCoin[k]);
    curRow[curSum].back().push_back(values[i]); // one less copy here too.
}

Even if it is readable to clean curRow = empty;, that may create allocation. Better to create a function

void Clean(vector<vector<vector<int>>>& vecs)
{
    for (auto& v : vecs) {
        v.clear();
    }
}