数据结构与算法(C++)– 动态规划(Dynamic Programming)

动态规划(Dynamic Programming)


理解动态规划的好文:https://www.sohu.com/a/153858619_466939


1、基础

**定义:**递归算法经常会有一些重复的计算,导致程序效率低。动态规划就是解决这个问题的方法。

动态规划的组成部分:

  • 最优子结构:分解问题
  • 边界:递归的终止条件
  • 状态转移方程:递归的方程

动态规划的解题步骤:

  • 分解问题,得到状态转移方程
  • 暴力搜索,找到冗余,通常用纯递归
  • 存储已经计算过的结果,通常用数组和字典
  • 编程时通常采用自底向上思想(递推)

2、走台阶问题

**题目:**有一座10级台阶的楼梯,从下往上走,每跨一步只能向上1级或者2级台阶。要求用程序来求出一共有多少种走法。

递归解法:

int solve(int n)
{
	if(n < 1)
		return 0;
	if(n == 1)
		return 1;
	if(n == 2)
		return 2;
	
	return solve(n-1)+solve(n-2);
}

备忘录解法:

int solve(int n,HashMapmap)
{
	if(n < 1)
		return 0;
	if(n == 1)
		return 1;
	if(n == 2)
		return 2;
	
	if(map.contains(n))
		return map.get(n);
	else
	{
		int value = solve(n-1)+solve(n-2);
		map.put(n, value);
		return value;
	}
}

动态规划解法:

int solve(int n,HashMapmap)
{
	if(n < 1)
		return 0;
	if(n == 1)
		return 1;
	if(n == 2)
		return 2;
	
	int a = 1;
	int b = 2:
	int temp = 0;
	
	for(int i=3; i<=n; i++)
	{
		temp = a + b;
		a = b;
		b = temp;
	}

	return temp;
}

题目改为一次最多能走k阶台阶

思路:其实是一样的,只要把边界和递归式扩展到 k 即可

// 边界条件, 转化为总共 k 阶台阶,可以任意走,到达k阶有2^(k-1)种走法
num[0] = 1
for(int i=1; i =< k; ++i)
	num[i] = power(2, i-1);

// 递推
for(int j = k+1; j

3、打家劫舍问题

**题目:**偷商店,不能连续偷两家,求偷的总额最大

备忘录解法:

vector save;    
int solve(vector& nums, int n)
{
	if(n < 0)
        return 0;
    if(save[n] >= 0)
        return save[n];
    
    save[n] = max(solve(nums, n-2)+nums[n], solve(nums, n-1));
           
    return save[n];
}
int rob(vector& nums) {
    int len = nums.size();
    for(int i=0; i

动态规划解法:

int rob(vector& nums) {
	int n = nums.size(); 
    if(n == 0)
        return 0;
    if(n == 1)
        return nums[0];
 
    vector save(n, -1);
    save[0] = nums[0];
    save[1] = max(nums[0], nums[1]);
    
    for(int i=2; i

4、01背包问题

题目: 给定 n 种物品和一个容量为 C 的背包,物品 i 的重量是 wi,其价值为 vi ,应该如何选择装入背包的物品,使得装入背包中的物品的总价值最大?

// 下标为0的元素,没有用到,置为0
int v[N]={0,8,10,6,3,7,2};
int w[N]={0,4,6,2,2,5,1};
int n=6,c=12;

int solve()
{
	// 需要知道的初始值是 m[i][0],m[0][j] 都等于0,所以数组都初始化为0
	int m[n+1][c+1] = {0};
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=c;j++)
        {
            if(j>=w[i])
                m[i][j]=max(m[i-1][j],m[i-1][j-w[i]]+v[i]);  
            else
                m[i][j]=m[i-1][j];
        }
    }
    return m[n][c]

可以进一步压缩空间,只用2x(c+1)或1x(c+1)大小的数组:

# 2x(c+1)数组
int m[2][c+1] = {0};
m[i%2][j]=max(m[(i-1)%2][j],m[(i-1)%2][j-w[i]]+v[i]);  

# 1x(c+1)大小的数组
int m[c+1] = {0};
m[j]=max(m[j],m[j-w[i]]+v[i]);  


5、硬币找零

题目: 硬币的种类值列表 coin_list,要找零钱 change 元,问最少需要几个硬币?

递归解法:

def find_coins(coin_list, change):
    min_coins = change
    if change in coin_list:
        return 1
    else:
        for i in [c for c in coin_list if c <= change]:
            num_coins = 1 + find_coins(coin_list, change-i)
            if num_coins < min_coins:
                min_coins = num_coins
    return min_coins

print(recDC([1,5,10,25], 63)

备忘录解法:

# save_list 缓存已经算过的零钱最少找币数,列表大小=最初需要找零的值+1,初始值都=0
def find_coins(coin_list, change, save_list):
    min_coins = change
    if change in coin_list:
        return 1
    elif save_list[change] > 0:
        return save_list[change]
    else:
        for i in [c for c in coin_list if c <= change]:
            num_coins = 1 + find_coins(coin_list, change-i, save_list)
            if num_coins < min_coins:
                min_coins = num_coins
                save_list[change] = min_coins
    return min_coins

print(recDC([1,5,10,25], 63, [0]*64))

动态规划解法:

# 递推
def find_coins(coins_list, change, min_coins_list):
    for num in range(change+1):
        num_coins = num
        for j in [c for c in coins_list if c <= num]:
            if min_coins_list[num-j] + 1 <= num_coins:
                num_coins = min_coins_list[num-j]+1
                min_coins_list[num] = num_coins
    return min_coins_list[change]


print(find_coins([1, 5, 10, 25], 63, [0]*64))

GOOD LUCK!


你可能感兴趣的:(数据结构与算法(C++,Python))