【问题标题】:all solutions to change making with dynamic programming使用动态规划进行变革的所有解决方案
【发布时间】:2014-08-27 18:23:08
【问题描述】:

我正在复习算法课的讲义,我开始思考这个问题:

给定不同类型的具有不同价值的硬币,找出所有硬币配置以加起来不重复。

在课堂上,我们解决了这个问题,找出所有可能的求和方式的数量以及求和的最少硬币数量。但是,我们从未尝试真正找到解决方案。

我正在考虑用动态规划解决这个问题。

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

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. 我不知道我的自下而上过程(使用循环替换递归)应该是什么样子。

欢迎任何帮助,我们将不胜感激!

感谢您的宝贵时间。

【问题讨论】:

  • 如果你可以使用动态编程来解决这个问题,这对我来说并不是很明显。如果我找到 50 美分,添加四分之一,然后在 25 美分上“递归”,找到 1 四分之一和 5 镍币。然后我回到开始,尝试 5 个 nickles,检查 25cents 的结果,找到计算的 2 个解,所以我说总共有 4 个解。但是 1 个四分之一 + 5 个镍和 5 个镍 + 1 个四分之一是重复的。
  • 可能有很多可能的变革解决方案,因此 DP 会有所帮助,但可能仍需要非常非常长的时间才能完成。你同意吗?
  • 我写了一个幼稚的版本来比较:coliru.stacked-crooked.com/a/d2c06ff6aa2ea45a
  • 你有没有在谷歌上寻找过“改变”?
  • 是的。我做到了。其中大多数只是“最小数量的硬币”和“总方式”。很少有人真正费心去寻找所有的解决方案,但他们都没有使用 DP。我欢迎建设性的 cmets,但只是问我是否在谷歌上搜索没有帮助......如果你能给我一个链接到你找到解决方案的地方,我将不胜感激。@tmyklebu

标签: c++ algorithm recursion dynamic-programming


【解决方案1】:

在 dp 解决方案中,您会生成一组中间状态,以及有多少种方法可以到达那里。那么你的答案就是最终处于成功状态的数字。

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

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

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

不过,如果您想稍微聪明一点,可以将其转换为可用的枚举。例如,您可以说“我知道有 4323431 个解决方案,第 432134 个解决方案是什么?”并且很快就能找到解决方案。

【讨论】:

  • 所以你的意思基本上是。除了一个二维数组 dp[i][j] ,其中 i 是剩余金额,j 是硬币的类型,我还需要另一个数据结构来存储我从一个状态到另一个状态时拿走的硬币。这样,当我完成计算所有可能方式的数量时,我可以蛮力,从完成状态开始并尝试递归地找到所有解决方案。这基本上就是你在说的吗?
  • @TianyuLang 听起来你明白了。它可以添加一个链接列表,其中包含您从哪些其他州到达一个州,并在每次您到达一个州时添加到它。
【解决方案2】:

很明显,您可以采用动态编程方法。不明显的是,在大多数情况下(取决于硬币的面额),您可以使用可能更有效的贪心算法。请参阅 Cormen、Leiserson、Rivest、Stein:算法简介第 2 版,问题 16.1。

【讨论】:

  • 是的。使用贪婪会更有效,但它不是一个通用的解决方案。因为如果你拥有的硬币类型是“特定的”,它会失败
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-09-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多