动态规划学习日记(一)

DP
这是我第一天学习动态规划的一点笔记和小小心得吧。
**概念:**递归+记忆化

标志:

​ 1、计数问题:有多少种方式?

​ 2、求最值:路径和问题、最值问题

​ 3、存在性:是否必胜?选k个数求sum值

动态规划三要素:

​ 1.最优子结构

​ 2.边界

​ 3.状态转移方程

例1、有2、5、7三面值硬币,凑27总数,求最少组合方案(最值问题)

**step 1:**确定状态

  • 终点

  • 子问题

    终点分析:总数27,设硬币数为k,终点硬币面值为ak,27-ak对应k-1枚硬币面值。

    子问题:最少用多少硬币凑27-Ak?

    目的:缩小问题规模

    设状态方程f(x)=最少硬币数

    终点可能为Ak = 2,5,7

    所以,f(27)= f(27-2||5||7)+1

    f(27)= min{f(27-2)+1,f(27-5)+1,f(27-7)+1}

    递归思路

    int f(int x){
        if(!x)return 0;
        int res = INT_MAX;
        if(x>=2){
            res=min(res, f(x-2)+1);
        }
        if(x>=5){
            res=min(res, f(x-5)+1);
        }
        if(x>=7){
            res=min(res, f(x-7)+1);
        }
        return res;
        
    }
    

    存在问题:

在这里插入图片描述

递归存在大量重复增加了算法的时间复杂度

改进思路:空间换时间,即用数组保存已经算过的值。

**step 2:**确定转移方程

f[x]= min{f[x-2]+1,f[x-5]+1,f[x-7]+1}([]表示一般以数组形式代替函数)

**step 3:**确定初始条件和边界

问题:例如<0情况和停止条件

1、拼不出

f[<0] = +无穷

例如:f[1] = min{f[-1]+1,f[x-4]+1,f[x-6]+1} = +无穷,表示无法凑1

2、初始化

f[0] = 0;

**step 4:**确定计算顺序

根据转移方程和初始条件确定顺序。

此题,转移方程为:f[x]= min{f[x-2]+1,f[x-5]+1,f[x-7]+1},初值为f[0] = 0;

因此顺序为从小到大。

例如:f[10] = min{f[8]+1,f[6]+1,f[2]+1},先计算f[8],f[6],f[2]保存即可快速得到f[10]

最终代码:

#include <iostream>
#include <climits>
#include <cstdlib>
#include <cstring>
using namespace std;
int a[100];
int f(int x){
	if(x < 0)return 1e9;
	int x1 = x - 2;
	int x2 = x - 5;
	int x3 = x - 7;
	int min_value;
	if(x2 >= 0)
	{
		 min_value = min(a[x - 2]+1, a[x - 5]+1);
	}
	else if(x1 >= 0)
	{
		min_value = a[x - 2]+1;
	}
	else
	{
		min_value = 1e9;
	}
	if(x >= 0){
		if(x3 >= 0){
			min_value = min(min_value, a[x - 7]+1);
		}
		else{
			min_value = min_value;
		}
		
	}
	return min_value;
}
int main() {
	memset(a, 1e9 ,sizeof(a));
	a[0] = 0;
	int i, n;
	cin>>n;
	for(i = 1;i <= n;i++)
	{
		a[i] = f(i);
	}	
	cout<<a[n]<<endl;	
	return 0;
}
发布了3 篇原创文章 · 获赞 3 · 访问量 40

猜你喜欢

转载自blog.csdn.net/weixin_43869733/article/details/105571227
今日推荐