最近在学习动态规划问题,看了几篇技术博客,大致对动态规划有了了解,我尝试以我个人的理解讲述何为动态规划,若有不同意见,欢迎交流讨论。
在我们学习一段时间后,必然会接触到递归的知识,递归使我们在对问题的求解时思路清晰,但同时它会浪费大量的时间与空间用于栈的开辟。同时,在处理问题时也会产生过多重复的部分。
例如接下来要求解的硬币找零问题 若用递归求解
int change_coin( int money, int number, int *coin){//money为当前找零币值 number为币值种类总数
cout << "money is :" << money << endl; //coin为int型数组 保存不同币值 由小到大
for(int i = 0; i < number; ++i){
if( money == coin[i]) //若找零币值恰好为现有币值中的一种 则直接找零
return 1; //则找零次数为1
}
if( money == 0) //money为0 则找零次数为0
return 0;
int min = 0x7fffffff; //min先设置为int型最大值
cout << min << endl; //min保存最少找零次数
for(int count = 0; count < number; ++count){
if( money - coin[count] < 0) //若当前币值小于保存币值 则无法找零
break; //直接跳出循环
int temp = change_coin(money-coin[count], number, coin, save);
//这里非常重要! 采用分治思想 将币值划分为两部分 一部分币值为coin[count]
//另一部分为money-coin[count]
//这里temp保存每次不同的找零的方法消耗的找零次数
cout << "temp is: " << temp << endl;
min = (temp+1 < min) ? temp+1 : min;//这里min为在所有对币值为money
//的找零方法最简的一种
}
cout << "min is: " << min << " save[money] is:" << save[money] << endl;
return min;//返回上一级
}
以下为个人实现的递归求解例子,只提供了求解函数,若想实现,请自行填写其余部分。
这里采用递归分治的思想
假设币值有10 5 1 那对一张12元面值的纸币找零可以分解为对一张1元面值的纸币以及一张11元面值的纸币求解
还可以分解为对一张5元面值的纸币以及一张7元面值的纸币求解 也可以分解为对一张10元的纸币以及一张2元的纸币求解
找出其中的规律 发现分解过程如同构造一棵树 1元不能再分解 直接返回次数 11元可以继续分解 分解流程如同分解12元 此过程交给计算机解决,最后在所有解决方案中进行筛选 得到最后的最优结果。
最优解得到,但明显可以再次优化,例如7元币值可以分解为5元与2元 12元可以分解为10元与2元 2元在这里计算了两次 浪费了时间,动态规划(dynamic programming 简称 DP)的作用是将之前的计算结果保存,让递归过程具有记忆性,这样每次遇到计算2元的问题,直接调出保存的数据就可以解决。这样的方法行之有效的避免了递归问题规模过大导致时间剧增的问题。
这里递归也未必是最有效的方法,若可以用递推解决则更好一些。我还未有成型的思路,就按下不表,留待以后解决。这里放上利用动态规划解决找零问题的代码,在前面递归的基础上增加了保存计算结果的步骤 完整代码如下:
# include <iostream>
# include <cstring>
using namespace std;
//利用DP选择找零问题
# define MaxSize 100
int change_coin( int money, int number, int *coin, int *save){
cout << "money is :" << money << endl;
for(int i = 0; i < number; ++i){
if( money == coin[i])
return 1;
}
if( money == 0)
return 0;
if( save[money] != -1)
return save[money];
int min = 0x7fffffff;
cout << min << endl;
for(int count = 0; count < number; ++count){
if( money - coin[count] < 0)
break;
int temp = change_coin(money-coin[count], number, coin, save);
cout << "temp is: " << temp << endl;
min = (temp+1 < min) ? temp+1 : min;
}
if( save[money] == -1)
save[money] = min;
cout << "min is: " << min << " save[money] is:" << save[money] << endl;
return min;
}
int main()
{
int coin[5] = {1,2,5,10,20};
int save[MaxSize+1];
memset( save, -1, (MaxSize+1)*sizeof(int));
cout << save[30] << endl;
cout << change_coin( 50, 5, coin, save);
return 0;
}