动态规划C++

引入题目

给定数组arr,arr中所有的值都为正数且不重复。每个值代表一种面值的货币,每种面值的货币可以使用任意张,再给定一个整数aim代表要找的钱数,求找钱有多少种方法。
分析:可以使用,暴力搜索方法、记忆搜索方法、动态规划方法、状态继续简化后的动态规划方法。

暴力搜索方法

arr = {5,10,25,1},aim=1000.
1.用0张5元货币,让[10,25,1]组成剩下的1000,最终方法数为res1.
2.用1张5元货币,让[10,25,1]组成剩下的995,最终方法数为res2.
… \ldots
201.用200张5元货币,让[10,25,1]组成剩下的0,最终方法数为res201.
其中res1,…,res201还可以继续递归分解。

int violentCoins(const int *arr,const 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 += violentCoins(arr, length, index + 1, aim - arr[index] * i);
		}
	}
	return res;

}

缺点:暴力搜索存在大量的重复计算,比如使用0张5元跟1张10元需要计算violentCoins(arr,length,2,990),而使用2张5元跟0张10元也需要计算violentCoins(arr,length,2,990)

记忆搜索法

结合暴力搜索法的缺点,可以设计一个map来存储是否计算过violentCoins(arr,length,2,990)。如果计算过可以直接使用,如果没有计算过则重新计算并将结果放入map中。

int memoryCoins(const int *arr, const int &length, int **map, int index, int aim) {//map == 0 表示没计算过, ==-1表示为0次。
	int res = 0;
	if (index == length) {
		res = (aim == 0 ? 1 : 0);
	}
	else {
		if (map[index][aim] == 0) {
			for (int i = 0; arr[index] * i <= aim; ++i) {
				res += memoryCoins(arr, length, map, index + 1, aim - arr[index] * i);
			}
			map[index][aim] = res == 0 ? -1 : res;
		}
		else {
			res = map[index][aim] == -1 ? 0 : map[index][aim];
		}
	}
	return res;
}

动态规划

如果arr长度为N,生成行数为N,列数为aim+1的矩阵dp。dp[i][j]的含义是在使用arr[0,…,i]货币的情况下,组成钱数j有多少种方法。
因此,第一列表示组成钱数为0的方法,第一列全为1。第一行表示arr[0]组成钱数j的方法,只有j=arr[0]的整数被时,dp[0][j]=1.
当要计算dp[i][j]时,考虑dp[i-1][j]+dp[i-1][j-arr[i]*1]+…+dp[i-1][j-arr[i]k].
依次计算每一行的值,最终,最右下角的值为所要求出的值。
求每一个位置的值时,都需要枚举上一行的值,时间复杂度为 O ( a i m ) O(aim) O(aim).dp中共有N
aim个位置,所以总体的时间复杂度为 O ( N ∗ a i m 2 ) O(N*aim^2) <

  • 1
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值