算法—动态规划学习笔记(1)入门

算法—动态规划学习笔记(1)入门

说实在的,搞算法以来,动态规划给我的感觉就像是高中数学倒数第三和第二道压轴题——老师讲了n多遍,你也知道要考啥(圆锥曲线和导数),基本原理也都懂。但是一到考试,只要稍稍变形,就不会了(说的就是本蒟蒻了,高考数学123,两道压轴题基本是空白)。

动态规划到底是什么

先来看一下百度百科的定义:

动态规划(dynamic programming)是运筹学的一个分支,是求解决策过程(decision process)最优化的数学方法。

也就是说,从本质上说,动态规划是一种数学统筹方法。不过由于易于用程序实现,且经常与其他算法问题相结合,因此也作为计算机算法分析与设计的一个主要内容。

那么,为什么叫动态规划呢?

规划的话,好理解,即对一个问题求最优解;那么动态呢?意思是这个问题中会有很多个转态,其中一个状态下便是整个问题的最优解。

动态规划的求解步骤

总得来说,动态规划分为三个步骤(当然也有人说四个步骤,且每个步骤说法都不一定一致):
1)阶段划分;

2)找出状态转移方程;

3)求解最优解。

是不是感觉说了通废话?确实如此,毕竟还没写代码不是吗?现在就以一个经典、简单的dp问题来入门:

问题(1)Coin-row Problem

There is a row of n coins whose values are some positive integers c₁, c₂,…,cn, not necessarily distinct. The goal is to pick up the maximum amount of money subject to the constraint that no two coins adjacent in the initial row can be picked up.

描述:有n个硬币,每一个的面值是

c_i

,且不一定重复,求在首排不相邻的情况下,找出能被捡起的最大硬币面额。

这个问题几乎是最经典、最简单的dp问题了。那么怎么考虑这个问题呢?

首先,我们假定已经选取了第n个硬币硬币cn,那么接下来的问题是:我到底选不选第n+1个硬币呢,

这时就要做比较了,看第n+1个硬币究竟选不选?怎么比较呢?即比较我选和不选两种转态下最大值,作为决策的依据。

如果令值为F(n)最大值,选了,则F(n)=cn+F(n-2)(不能从相邻的下手);不选,则为F(n)=F(n-1),即跳过从下一个开始。

所以转态转移方程就很简单了:

F(n)=max[{ c_n+F(n-2),F(n-1)}] 

F(0)=0,F(1)=c_1

因此程序如下:

 #include <iostream>  
    using namespace std;  
    int n, dp[10001], i, x[10001];  
    #define max(a,b) a>b?a:b  
    int main()  
    {  
        cin >> n;  
        for (i = 1; i <= n; i++) cin >> x[i];  
        dp[1] = x[1];  
        for (i = 2; i <= n; i++)  
            dp[i] = max(dp[i - 1], dp[i - 2] + x[i]);  
        cout << dp[n] << "\r\n";  
        return 0;  
    }  

回顾与反思

从刚才这个很简单的动态规划,我们可以看出一点点端倪:

之所以叫动态规划,是因为,这个决策过程是一个动态的过程——比如刚才的挑硬币问题。每一步挑不挑都是一个不确定的转态,

再注意体会一下“状态”这个词,为什么叫状态?因为问题决策的过程中,有多个步骤和可能性,每一个步骤或可能性都有一定的共性(大多时候,可以用一个方程表示出来)。

当状态转移确定后,问题就好下手了:将所有的状态找一遍,必然存在着问题的最优解。

以上,便是动态规划的入门级思想。当然,dp还有多个细节及问题种类,比如减枝、0-1背包问题、树形dp等等。以后我也会陆续作讨论。

若写的有什么问题,还请指正:)

发布了15 篇原创文章 · 获赞 16 · 访问量 1087

猜你喜欢

转载自blog.csdn.net/weixin_44522586/article/details/103263371