我试图解决硬币变化的基本动态规划问题:
给定值N,如果我们想要改变N美分,并且我们每个S = {S1,S2,..,Sm}值的硬币都有无限供应,我们可以通过多少方式进行更改?硬币的顺序无关紧要。 这是我可以创建的解决方案:
我用所有三种一般方法解决了这个问题。即。递归,DP记忆和DP制表。
C ++实现:https://ideone.com/14bBIv
#include <iostream>
#include <cstring>
#include <sys/time.h>
#include <limits.h>
using namespace std;
int max2(int a, int b)
{
return (a>b) ? a : b;
}
int max3(int a, int b, int c)
{
if(a>c)
return (a>b) ? a : b;
else
return (c>b) ? c : b;
}
int min3(int a, int b, int c)
{
if(a<c)
return (a<b) ? a : b;
else
return (c<b) ? c : b;
}
int Coins_rec(int * S, int m, int n)
{
if(n == 0)
return 1;
if(n < 0)
return 0;
if(m<=0 && n >=0){
return 0;
}
return ( Coins_rec( S, m-1, n ) + Coins_rec( S, m, n - S[m-1] ) );
}
int memo[101][101] ;
int Coins_memoization(int * S, int m, int n)
{
if(n == 0)
return 1;
if(n < 0)
return 0;
if(m<=0 && n >=0){
return 0;
}
if(memo[m][n] !=-1) {
return memo[m][n];
} else
{
memo[m][n] = ( Coins_rec( S, m-1, n ) + Coins_rec( S, m, n - S[m-1] ) );
return memo[m][n];
}
}
int Coins_tabulation (int * S, int m, int n)
{
int L[m+1][n+1] ; //L[i][j] -> Minimum number of 'i' different types of coins required to make final amonut j
int i, j;
L[0][0] = 1;
for(i=0;i<=m;i++){
for(j=0;j<=n;j++){
if (i == 0 && j >= 0) {
L[0][j] = 0;
} else if (i > 0 && j == 0) {
L[i][0] = 1;
} else {
L[i][j] = ( (i >= 1) ? L[i-1][j] : 0 ) + ( (j - S[i-1] >=0) ? L[i][j - S[i-1]] : 0 ) ;
}
}
}
return L[m][n];
} // ----- end of function Coins_tabulation -----
int main() {
int arr[] = {2, 5, 3, 6};
int m = sizeof(arr)/sizeof(arr[0]);
int n;
cout << "Enter the amount" << endl;
cin >> n;
for(int i=0; i<=101; i++){
for(int j=0; j<=101; j++){
memo[i][j] = -1;
}
}
struct timeval t0; gettimeofday(&t0 , NULL);
cout << "Number of Ways = " << Coins_rec(arr, m, n) << endl;
struct timeval t1; gettimeofday(&t1 , NULL);
cout << "recursion : " << (t1.tv_sec - t0.tv_sec) << " seconds and " << (t1.tv_usec - t0.tv_usec) << " microseconds" << endl;
cout << "Number of Ways (Memoization) = " << Coins_memoization(arr, m, n) << endl;
struct timeval t2; gettimeofday(&t2 , NULL);
cout << "memoization : " << (t2.tv_sec - t1.tv_sec) << " seconds and " << (t2.tv_usec - t1.tv_usec) << " microseconds" << endl;
cout << "Number of Ways (Tabulation) = " << Coins_tabulation(arr, m, n) << endl;
struct timeval t3; gettimeofday(&t3 , NULL);
cout << "tabulation : " << (t3.tv_sec - t2.tv_sec) << " seconds and " << (t3.tv_usec - t2.tv_usec) << " microseconds" << endl;
return 0;
}
。 以下是结果: (通过标准输入获取的输入)
For n = 10
recursion : 0 seconds and 14 microseconds
memoization : 0 seconds and 17 microseconds
tabulation : 0 seconds and 17 microseconds
For n =100
recursion : 0 seconds and 1300 microseconds
memoization : 0 seconds and 1270 microseconds
tabulation : 0 seconds and 35 microseconds
我无法理解的是,为什么对于稍微大一点的输入,memoization花费的时间几乎与简单的递归解决方案相同?在这种情况下,记忆的记忆时间是否比制表更少,因为它会从矩阵中跳过许多无用的计算,而这些计算必须在制表的情况下完成?
答案 0 :(得分:4)
这是因为你实际上在memoization算法的递归步骤中递归到Coins_rec
而不是Coins_memoization
。一旦你解决了这个问题,你会发现,对于更大的数字,memoization确实和制表一样快。例如,以下是我机器上100的结果:
100
recursion : 0 seconds and 914 microseconds
memoization : 0 seconds and 18 microseconds
tabulation : 0 seconds and 16 microseconds
答案 1 :(得分:1)
你应该期望memoization渐进地更快,但是对于以微秒为单位解决的微小输入,所有的赌注都是关闭的,实际上更快将是高度依赖硬件/系统的。