所有通过动态编程改变的解决方案

时间:2014-08-27 18:23:09

标签: c++ algorithm recursion dynamic-programming

我正在查看我的算法类的讲义,我开始考虑这个问题:

鉴于具有不同价值的不同类型的硬币,找到所有硬币配置以加起来一定金额而不重复。

在课堂上,我们解决了问题,找到所有可能的总和方式的数量和总和的最少数量的硬币。但是,我们从未试图找到解决方案。

我正在考虑用动态编程来解决这个问题。

我带了递归版本(为简单起见,我只打印解决方案):

void solve(vector<string>& result, string& currSoln, int index, int target, vector<int>& coins)
{
    if(target < 0)
    {
        return;
    }

    if(target == 0)
    {
        result.push_back(currSoln);
    }

    for(int i = index; i < coins.size(); ++i)
    {
        stringstream ss;
        ss << coins[i];
        string newCurrSoln = currSoln + ss.str() + " ";
        solve(result, newCurrSoln, i, target - coins[i], coins);
    }
}

但是,在尝试使用DP解决问题时我遇到了困难。 我有两个主要障碍:

  1. 我不知道我应该用什么数据结构存储以前的答案
  2. 我不知道我的自下而上的程序(使用循环来代替递归)应该是什么样的。
  3. 欢迎任何帮助,并欢迎一些代码!

    感谢您的时间。

2 个答案:

答案 0 :(得分:1)

在dp解决方案中,您会生成一组中间状态,以及到达目的地的方式。那么你的答案就是成功状态下的数字。

因此,对于变更计数,状态是您需要进行特定的更改。计数是改变方式的数量。成功的状态是你做出了正确的改变。

要从计算解决方案到枚举它们,您需要保留这些中间状态,并在转换到该状态的所有状态的每个状态中保留记录 - 以及有关如何的信息。 (在更改计数的情况下,您添加的硬币将如何。)

现在有了这些信息,您可以从成功状态开始,然后递归地通过dp数据结构向后来实际找到解决方案而不是计数。好消息是,你所有的递归工作都是有效的 - 你总是只看着成功的路径,所以不要浪费时间在那些不能工作的事情上。但如果有十亿个解决方案,那么没有任何皇家捷径可以快速打印出十亿个解决方案。

但是,如果您希望变得有点聪明,可以将其转换为可用的枚举。例如,您可以说&#34;我知道有4323431个解决方案,432134是什么?&#34;找到解决方案的速度很快。

答案 1 :(得分:0)

很明显,您可以采用动态编程方法。在大多数情况下(取决于硬币的面额),您可以使用贪婪算法,这可能更有效率,这一点并不明显。参见Cormen,Leiserson,Rivest,Stein:算法导论第2版,问题16.1。