动态规划

动态规划

动态规划就是消除冗余,直接使用之前算过的值,而不需要再次重新计算的方式。

记忆搜索

1.记忆搜索是某种形态的动态规划。

2.记忆化搜索方法不关心达到目的的路径。

3.动态规划是规定好每一个递归过程的计算顺序,以此计算,后面的计算依赖前面的计算

4.两者都是空间换时间,也都有枚举过程,区别在于动态规划有计算顺序(可以进一步优化),而记忆搜索不用规定。


例子:

给定数组arr,arr中所有的值都为正数且不重复。每个值都代表一种面值的货币,每种面值的货币可以使用任意张,再给定一个整数aim代表要找的钱总数,求换钱有多少种方法?

arr={5、10、25、1} ,aim=1000。

分析:

1.用0张5元,用剩下的{10、25、1}三种面值的货币置换剩下的1000元的方法数-------------res0

2.用1张5元,用剩下的{10、25、1}三种面值的货币置换剩下的995元的方法数-------------res1

.....................................

201.用200张5元,用剩下的{10、25、1}三种面值的货币置换剩下的0元的方法数-------------res200

使用p(arr,index,aim)返回用数组arr{index,...,N-1}中的所有面值货币组成aim的方法数

暴力搜索:

暴力搜索之所以暴力就是因为其重复了太多的冗余操作,重复计算。试想当使用了2张5元和0张10元后,用{25、1}置换剩下的990的方法数为p(arr,2,990)

而当使用了0张5元和1张10元后,用{25、1}置换剩下的990的方法数也是p(arr,2,990)

这样的情况会大量的存在,也就造成了大量的计算冗余所以不使用。

//暴力搜索
int coins1(int arr[], int length, int aim)
{
	std::cout << "arr长度为" << *arr << std::endl;
	if (arr ==NULL || length == 0 || aim < 0)
	{
		return 0;
	}
	return process1(arr, length ,0 , aim);
}

int process1(int arr[], int length, int index, int aim)
{
	int res = 0;
	if (index == length)
	{
		res = aim == 0 ? 1 : 0;
	}
	else
	{
		for (int i=0;arr[index]*i<=aim;i++)
		{
			res += process1(arr,length ,index + 1, aim - arr[index] * i);
		}
	}
	return res;
}

记忆搜索:

迭代的变化量是index和aim,所以可以将index和aim作为key,将每次迭代的结果放置一个map中。当进入新的迭代时,先检查map中是否有此key对应的值,若有则取出value即可,否则便计算后再将key和value一同放入map中。

int coins2(int arr[], int length, int aim)
{
	if (arr == NULL || length == 0 || aim < 0)
	{
		return 0;
	}
	std::vector<std::vector<int>> Map(length+1,std::vector<int>(aim+1));
	return process2(arr, length, 0, aim ,Map);
}

int process2(int arr[], int length, int index, int aim, std::vector<std::vector<int>> Map)
{
	int res = 0;
	if (index == length)
	{
		res = aim == 0 ? 1 : 0;
	}
	else
	{
		int mapValue = 0;
		for (int i=0;arr[index]*i<aim;++i)
		{
			mapValue = Map[index+1][ aim - arr[index] * i ];
			if (mapValue != 0)
			{
				res += mapValue == -1 ? 0 : mapValue;
			}
			else
			{
				res += process2(arr, length, index + 1, aim - arr[index] * i, Map);
			}
		}
		
	}
	Map[index][aim] = res == 0 ? -1 : res;
	return res;
}

动态规划:

假设arr长度为N,生成行数为N,列数为aim+1的矩阵dp。

dp[i][j]的含义是在使用arr[0...i]货币的情况下,组成钱数j有多少种方法

则可以知道第一列表示组成钱数为0的方法数为1(即不使用任何面值的货币),第一行为使用arr[0]组成钱数为j的方法数,可知只有当j为arr[0]这个数的整数倍时才有可能用arr[0]组成j。所以第一行在j等于arr[0]的整数倍处为1,其余为0。


d[i][j]可以表示为:


由此可知d[i][j]=dp[i-1][j]+dp[i-1][j-1*arr[i]]+dp[i-1][j-2*arr[i]]+...

可知dp[i][j-1*arr[i]]=dp[i-1][j-1*arr[i]]+dp[i-1][j-2*arr[i]]+...

所以上述过程可以化简为:

d[i][j]=dp[i-1][j]+dp[i][j-1*arr[i]]

所以整个问题得到了化简。

这就是动态规划的方法。

//动态规划
int coins3(int arr[], int length, int aim)
{
	if (arr == NULL || length == 0 || aim < 0)
	{
		return 0;
	}
	return process3(arr, length-1, aim);
}
int process3(int arr[],  int i,int j)
{
	
	if ( i == 0)
	{
		if ( j%arr[0] ==0 )
		{
			return 1;
		}
		else
		{
			return 0;
		}
	}
	else if ( j == 0 )
	{
		return 1;
	}
	else if (i<0 || j<0)
	{
		return 0;
	}
	else
	{
		return (process3(arr,   i-1,j) + process3(arr,  i , j-arr[i]));
	}
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值