您的位置:首页 > 其它

最少硬币找零问题

2011-12-26 12:50 267 查看
Problem:
Give you the coins, and the total amount of money to change,find a solution for this change which minimize the number of coins needed.

Example:

Coins[] = {1,3,9,10};

Money = 15

Solution[] = {3,3,9}

分析:这道题类似于背包问题,背包问题的思路是,考虑第j个物品装入容量为j的背包中,此时有两只情况,j装得进去,j装不进去,然后推到出动态规划状态转移方程。对于这个问题,我们也可以用相同的思路来思考。

归纳(第一次尝试):假设已知如何解决money为i-1,硬币种类为j-1的子问题,记为P(i-1,j-1)。

接下来怎么办呢,如何推到出P(i,j)?在这样的归纳基础下面,我们不得不从i-1,j-1归纳出i,j来,也就是说我们在这个过程中同时对两个变量进行了归纳,这样会导致问题变得有些复杂,无从下手。(心得:如果在某一处卡住了,回头看看这个过程是否有漏洞;其次在读读题目,看看是否漏掉一些信息,题目的最终结果是求什么,怎么来表示这个结果。)

归纳(第二次尝试):假设已知如何解决P(i,j-1)。这个归纳就比上面那个好一些,而且容易思考下一步的问题,因为现在我们只要考虑第j种硬币,从而看看能否推导出P(i,j)。对于第j中硬币,我们有两种策略,一是使用第j种硬币不能使当前结果更优;二是使用第j种硬币能使当前结果更优,下面重点分析第二种情况。

现在问题变为要使用第j种硬币,那么该用多少呢?记Cj= i/j,这是单独使用第j种硬币需要的数量,Vj表示j的面值。接取Cj是最优的吗?怎样证明这样不行?

设Rj = i-Cj*Vj,使用Cj后剩余的money;设Qj= i-(Cj-1)*Vj, 使用Cj-1个硬币后剩余的money此时Qj = Rj + Vj。那么接下来找零Rj使用的硬币一定比找零Qj的硬币少吗?这个不一定,因为Rj相比Qj跟小,这个意味着需要更多的零钱。比如Rj=2,Qj=12,此时都需要两个硬币(1,1和3,9),而最终结果是开始选择Cj-1比选择Cj的少一个。所以在这里,应当全面的考察由1,2,…Cj的情况。

P(i,j-1)=MIN{ P(i,j-1),P(i-k*Vj,j-1)+k}(k=1,2,…Cj)

参考代码:

有些动态规划的中间结果不需要多维数组来存放,有些情况只需要一维数组即可,知道如何写和自己动手写出来,得到的信息不一样,唯有动手才能体会更多

int leastnums(int coins[], int numcoin,int money)
{
if(numcoin < 1 || money < 0 || coins[0] > money)
return -1;

int *res = new int[money+1];

//初始化,只用第一个硬币
for(int i = 1; i < money+1; ++i)
res[i] = money / coins[0];

for(int j = 1; j < numcoin; ++j)
{
for( int i = coins[j]; i < money+1; ++i)
{
int k = i / coins[j];
if( i%coins[j]==0)
{
res[i] = k;
continue;
}
while(k)
{
int left = i - k*coins[j];
if(res[left]+k < res[i])
res[i] = res[left]+k;
--k;
}
}//end for
}//end for

return res[money];
}

int _tmain(int argc, _TCHAR* argv[])
{
int coins[]={1,3,9,10};
int lnum = leastnums(coins,4,22);
return 0;
}


总结:动态规划还可以在优化吗?动态规划有一个变形,叫备忘录法。备忘录法也是用一个表格来保存已解决的子问题的答案,并通过记忆化搜索来避免计算一些不可能到达的状态。其实在写动态规划code过程中,有些地方也可以进行改进和优化,并非没一个动态规划都可以优化。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: