算法之动态规划详解

3 篇文章 0 订阅

一. 基本知识

  • 动态规划问题的一般形式就是求最值;求动态规划的核心问题是穷举。
  • 动态规划三要素:重叠子问题、最优子结构、状态转移方程(最难的)。
  • 重叠子问题可以通过备忘录或者DP table 来优化穷举,避免不必要的计算。
  • 最优子结构通过子问题的最值得到原问题的最值。
  • 动态规划的一般流程就是三步:暴力的递归解法 -> 带备忘录的递归解法->迭代的动态规划解法

二. 辅助思考状态转移方程的思维框架

明确[状态] -> 定义dp数组/函数的含义 -> 明确[选择] -> 明确 base case

三. 以凑零钱为例子

给你 k 种面值的硬币,面值分别为 c1, c2 ... ck ,每种硬币的数量无限,再给一个总金额 amount ,
问你最少需要几枚硬币凑出这个金额,如果不可能凑出,算法返回 -1。
  1. 暴力递归
  • 明确[状态] :就是原问题和子问题中变化的变量。由于硬币数量无限,所以唯一的状态就是目标金额amount.;
  • 确定dp函数的定义:当前的目标金额是n,至少需要dp(n)个硬币凑出该金额;
  • 确定「选择」并择优:也就是对于每个状态,可以做出什么选择改变当前状态。具体到这个问题,⽆论当的⽬标⾦额是多少,选择就是从⾯额列表coins中选择⼀个硬币,然后⽬标⾦额就会减少;
  • 最后明确 base case,显然⽬标⾦额为 0 时,所需硬币数量为 0;当⽬标⾦额⼩于 0 时,⽆解,返回 -1:
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
int dp(vector<int>& coins, int n) {
	if (n == 0)
		return 0;
	if (n < 0)
		return -1;
	int res = INT_MAX;
	for (int coin : coins) {
		int temp = dp(coins, n - coin);
		if (temp == -1)
			continue;
		res = min(res, 1 + temp);
	}
	return res == INT_MAX ? -1 : res;
}
int coinChange(vector<int>& coins, int amount) {
	return dp(coins, amount);
}
int main()
{
	vector<int> coins = { 1,2,5 };
	int amount = 11;
	cout << coinChange(coins, amount) << endl;
	system("pause");
	return 0;
}

在这里插入图片描述

  • 以上可以 得到状态转移方程:
    在这里插入图片描述
  1. 带备忘录的递归
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
int dp(vector<int>& coins, int n, vector<int>& count) {
	if (n == 0)
		return 0;
	if (n < 0)
		return -1;
	if (count[n - 1] != 0)
		return count[n - 1];
	int res = INT_MAX;
	for (int coin : coins) {
		int temp = dp(coins, n - coin, count);
		if (temp >= 0 && temp < res)
			res = 1 + temp;
	}
	count[n - 1] = (res == INT_MAX) ? -1 : res;
	return count[n - 1];
}
int coinChange(vector<int>& coins, int amount) {
	if (amount < 1)
		return 0;
	vector<int> count(amount,0);
	return dp(coins, amount, count);
}
int main()
{
	vector<int> coins = { 1,2,5 };
	int amount = 11;
	cout << coinChange(coins, amount) << endl;
	system("pause");
	return 0;
}
  1. dp数组的迭代解法
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
int coinChange(vector<int>& coins, int amount) {
	vector<int> dp(amount + 1, amount + 1);
	dp[0] = 0;
	for (int i = 0; i < dp.size(); ++i) {
		for (int coin : coins) {
			if (i - coin < 0)
				continue;
			dp[i] = min(dp[i], 1 + dp[i - coin]);
		}
	}
	return (dp[amount] == amount + 1) ? -1 : dp[amount];
}
int main()
{
	vector<int> coins = { 1,2,5 };
	int amount = 11;
	cout << coinChange(coins, amount) << endl;
	system("pause");
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值