minimum coin change problem是NP完全问题,但对于某些硬币组,贪婪算法(首先选择最大面额)起作用。给定一组表示硬币值的整数,确定贪心算法是否足够的最快算法是什么?一个显而易见的方法是建立你的动态编程解决方案,直到最大面额,并看看它是否产生比贪婪方式更好的解决方案。但有没有更快的“数学方法”来检测它?
答案 0 :(得分:2)
我最近提出了一个解决方案,似乎表明如果满足给定的2个条件,贪婪算法将产生最优解。
a)G.C.D(除1以外的所有面额)=第2个最小面额。
b)任何2个连续面额的总和必须小于连续第3个面额。
例如。 c2 + c3< C4。
(其中c1 = 1; c2,c3,c4是按升序排列的硬币面额)。
我知道这不是一个完整的解决方案。但是,我相信如果满足这两个条件,贪婪算法将产生最佳解决方案。
答案 1 :(得分:0)
如果以下选择产生目标数量,则贪婪算法将起作用:(可能有更复杂的格式可以使用,但这很简单且线性检查)
让1
代表选中。从最大面额的集合看起来像:
11...1100...00100...00
即,从最大的和选择的单个其他元素中选择零个或多个。
为什么这是最优的很容易证明。设C =从最大的s中选择的s元素,然后我们知道C为任何s或更少的元素产生最大可能的总和,因为,如果要替换C中的任何元素,则必须使用较低的元素。有价值的元素,因为已经选择了最大的元素(并且删除也会明显降低成本)。由于C产生的值小于目标值(因为需要另外一个元素),我们需要至少一个其他元素才能到达目标,因此到达目标所需的最小元素数量是s + 1,结论证明。
您需要O(n)来评估这一点,这可以通过以下方式完成:(在Java中)
int[] sortedCoins = ...;
int sum = 0, selectionCount = 0, i = 0;
for (; i < sortedCoins.length; i++)
{
sum += sortedCoins[i];
if (sum >= target) break;
selectionCount++;
}
sum -= sortedCoins[i]; // we added the element to push us over, so remove it again
for (; i < sortedCoins.length; i++)
{
if (sum + sortedCoins[i] == target)
return selectionCount+1;
}
return -1; // not found
哦,还有一个小例子,其中target = 0需要检查是否允许。
以上需要一个目标金额,如果你想检查很多目标金额,你可以用O(n ^ 2)中的上述方法生成所有可能的金额,并且有一个计算总和的地图,只是做查找以获取值(如果存在)。
编辑:分支和绑定
作为上述的扩展和DP的替代,我建议用分支和绑定贪婪地处理暴力。使用与上面类似的参数,您知道如果bestCoins具有值(currentSum + (bestCoins - currentCoins) * currentItem.value) < target
,则可以跳过当前路径。
请注意,这可能会在某些问题上失败并且在其他问题上表现得非常好(我认为这在很大程度上取决于我们找到合适解决方案的时间)。为了避免永远消失,您可以将最小可能数量的硬币存储在最佳解决方案中(从查看第一个元素得到,直到我们到达目标,类似于上面),并切断远离此的路径。
如果做得对,你应该可以在短时间内运行它,如果它运行完成并且我们有一个解决方案,我们有最佳解决方案,如果没有,我们没有损失太多时间,可以运行另一种算法,如DP。
答案 2 :(得分:0)
Pearson 的论文 A polynomial-time algorithm for the change-making problem Operation Research Letters 33:3(2005 年 5 月),第 231-234 页给出了一个多项式时间算法来找到贪婪算法(如果存在)的最小反例。不需要穷举搜索,他的主要定理大大缩小了候选集。
答案 3 :(得分:-3)
贪婪的解决方案仅适用于某些面额,相对于进行更改的特定金额。
添加后退步骤,然后它不再是贪婪,最终会考虑找到所有可能的解决方案,因此它既不是动态编程问题。
示例:考虑造币= [2,5],要更改的金额为16。 一个贪婪的第一个解决方案首先自然地寻找最大面额,[5,5,5]然后它停留在1。 回溯步骤产生另一种解决方案[5,5,2,2,2]。