动态规划入门(DP)上篇

动态规划

Dynamic Programming(此处“Programming”为“规划”,而非指“程序”、“编程”),研究多步决策过程最优化问题的一种数学方法,英文缩写DP。在动态规划中,为了寻找一个问题的最优解(即最优决策过程),将整个问题划分成若干个相应的阶段,并在每个阶段都根据先前所作出的决策作出当前阶段最优决策,进而得出整个问题的最优解。

能采用动态规划求解的问题的一般要具有3个性质:

1.最优化原理:如果问题的最优解所包含的子问题的解也是最优的,就称该问题具有最优子结构,即满足最优化原理。

2.无后效性:即某阶段状态一旦确定,就不受这个状态以后决策的影响。也就是说,某状态以后的过程不会影响以前的状态,只与当前状态有关。

3.有重叠子问题:即子问题之间是不独立的,一个子问题在下一阶段决策中可能被多次使用到。(该性质并不是动态规划适用的必要条件,但是如果没有这条性质,动态规划算法同其他算法相比就不具备优势)

解题步骤:

1.拆分问题

2.定义状态(并找出初状态)

3.状态转移方程

例1:

有一个由非负整数组成的三角形,第一行只有一个数,除了最下行之外每个数的左下方和右下方各有一个数,如图所示; 从第一行的数开始,每次可以往左下或右下走一格,直到走到最下行,把沿途经过的数全部加起来,如何走才能使得这个和尽量大。

把当前位置(i,j)看成一个状态,然后定义状态(i,j)的指标函数d(i,j)为从格子(i,j)出发时能得到的最大和(包括格子(i,j)本身的值)。在此状态的定义下,原问题的解为d(i,j)。

状态转移方程:
d(i ; j) = a(i ; j) + max fd(i + 1; j); d(i + 1; j + 1)}

实现方式:

1.递归计算

int solve(int i,int j)
{
    return a[i][j]+(i==n?0:max(solve(i+1,j),solve(i+1,j+1)));
}


2.记忆化搜索

memset(d,-1,sizeof(d));
int solve(int i,int j)
{
    if(d[i][j]>=0) return d[i][j];
        return d[i][j]=a[i][j]+(i==n?0:max(solve(i+1,j),solve(i+1,j+1)));
}


3.递推计算
 

//递推
int i,j;
for(j=1;j<=n;j++) 
    d[n][j]=a[n][j];

//递推 
for(i=n-1;i>=1;i--)
{
    for(j=1;j<=i;j++)
    {
        d[i][j]=a[i][j]+max(d[i+1][j],d[i+1][j+1]);
    }
}

例2:

如果我们有面值为1元、3元和5元的硬币若干枚,如何用最少的硬币凑够11元? (表面上这道题可以用贪心算法,但贪心算法无法保证可以求出解,比如1元换成2元的时候)

状态:d(i)表示凑够i元需要的最少硬币数量
状态转移方程:
d(i) = min {d(i - vj ) + 1},其中i - vj >= 0,vj表示第j个硬币的面值。

for(int i=1;i<=n;i++)
{
    for(int j=1;j<=m;j++)
    {
        if(i>=v[j])
        {
            d[i]=min(d[i],d[i-v[j]]+1);
        }
    }
}


 
 

猜你喜欢

转载自blog.csdn.net/qq_42756958/article/details/81504490