一,什么是动态规划
动态规划算法是通过拆分问题,定义问题状态和状态之间的关系,使得问题能够以递推(或者说分治)的方式去解决。
动态规划算法将问题分割成不同阶段(子问题),在解决每一个子问题时都为后一个子问题做下了铺垫。一步步解决所有的子问题后,最后一个子问题的答案就是整个问题的答案。
动态规划算法有点类似于递归,但他的核心方法是**“填表—查表”**,动态规划算法牺牲了运行内存来节省运行时间。
二,动态规划算法适用的条件
能采用动态规划求解的问题的一般要具有3个性质:
(1)最优化原理:如果问题的最优解所包含的子问题的解也是最优的,就称该问题具有最优子结构,即满足最优化原理。
(2)无后效性:即某阶段状态一旦确定,就不受这个状态以后决策的影响。也就是说,某状态以后的过程不会影响以前的状态,只与当前状态有关。
(3)有重叠子问题:即子问题之间是不独立的,一个子问题在下一阶段决策中可能被多次使用到。(该性质并不是动态规划适用的必要条件,但是如果没有这条性质,动态规划算法同其他算法相比就不具备优势)
三,如何运用动态规划算法解决问题
一般情况下,我们运用动态规划算法解决问题的第一步都是——根据题目内容构造合适的多维数组
为什么要构建多维数组?
我们将每个阶段的解储存在数组的每一个元素之中,在解决后面的子问题时通过查表即查询前置数组元素的值来为后面阶段问题提供解。这节省的整个程序的运行时间,不用在解决后置问题是仍需要从最小的子问题开始递推。
第二步则是构建状态转移方程。状态转移就是根据上一阶段的状态和决策来导出本阶段的状态。
在解决每一个问题时我们都需要通过前面阶段子问题的解来得出答案。
其他的,你需要去关注边界条件。
四,实例
接下来我通过分析一下我最近做过的题来清晰的讲述如何运用动态规划算法解决问题。
题目描述 Description
某天,妮妮在街上闲逛。他在超市里看到促销广告:商品大降价。于是他很高兴地拿着篮子购物去了。
已知商场内有n中商品。每种商品的重量为w千克,价格为v,价值为t。此种商品有h件。
注意:此商场有一个奇怪的规定。每种物品要么不买,要么买1件或h件。妮妮带了y元。妮妮最多能扛x千克的物品。请帮妮妮求出他最多能获得的价值。(不允许抢劫)
首先,很清晰的能看到这道题的边界的条件,物品的种类,还有妮妮带的钱数和她的承重量,我们需要依靠这个来建立状态转移方程并得出答案。
然后,要构建多维数组,有三个边界条件,所以我们需要构建一个三维数组—p。
其中
p[i][j][k]
数组中每一个元素的意义为,在妮妮带了j元钱并且载重量为k时,她在前i个物品中能带走的最大价值量。
因为这个问题物品有三这个拿法,不拿、拿一个或全拿走。所以略微复杂一点。
if(j>=q[i-1].a*q[i-1].d&&k>=q[i-1].b*q[i-1].d)
{
p[i][j][k]=choose(p[i-1][j][k],p[i-1][j-q[i-1].a][k-q[i-1].b]+q[i-1].c,p[i-1][j-q[i-1].a*q[i-1].d][k-q[i-1].b*q[i-1].d]+q[i-1].c*q[i-1].d);
}
else
{
if(j>=q[i-1].a&&k>=q[i-1].b)
{
p[i][j][k]=choose(p[i-1][j][k],p[i-1][j-q[i-1].a][k-q[i-1].b]+q[i-1].c,0);
}
else
{
p[i][j][k]=p[i-1][j][k];
}
判断能否全放入,如果不能全放入能否放入一个。
整体代码:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<math.h>
struct one
{
int a;
int b;
int c;
int d;
}*q;
int ***p;
int n,x,y;
void input();
int choose(int a,int b,int c);
void one();
int main(void)
{
input();
one();
printf("%d",p[n][x][y]);
return 0;
}
void input()
{
int i,j;
scanf("%d %d %d",&n,&x,&y);
q=(struct one*)malloc(n*sizeof(struct one));
p=(int***)malloc((n+1)*sizeof(struct one));
for(i=0;i<n+1;i++)
{
p[i]=(int**)malloc((x+1)*sizeof(int*));
for(j=0;j<x+1;j++)
{
p[i][j]=(int*)malloc((y+1)*sizeof(int));
}
}
for(i=0;i<n+1;i++)
{
for(j=0;j<x+1;j++)
{
p[i][j][0]=0;
}
}
for(i=0;i<n+1;i++)
{
for(j=0;j<y+1;j++)
{
p[i][0][j]=0;
}
}
for(i=0;i<x+1;i++)
{
for(j=0;j<y+1;j++)
{
p[0][i][j]=0;
}
}
for(i=0;i<n;i++)
{
scanf("%d %d %d %d",&q[i].a,&q[i].b,&q[i].c,&q[i].d);
}
}
int choose(int a,int b,int c)
{
if(a>=b&&a>=c)
return a;
if(b>=a&&b>=c)
return b;
return c;
}
void one()
{
int i,j,k;
for(i=1;i<n+1;i++)
{
for(j=1;j<x+1;j++)//重量
{
for(k=1;k<y+1;k++)//钱数
{
if(j>=q[i-1].a*q[i-1].d&&k>=q[i-1].b*q[i-1].d)
{
p[i][j][k]=choose(p[i-1][j][k],p[i-1][j-q[i-1].a][k-q[i-1].b]+q[i-1].c,p[i-1][j-q[i-1].a*q[i-1].d][k-q[i-1].b*q[i-1].d]+q[i-1].c*q[i-1].d);
}
else
{
if(j>=q[i-1].a&&k>=q[i-1].b)
{
p[i][j][k]=choose(p[i-1][j][k],p[i-1][j-q[i-1].a][k-q[i-1].b]+q[i-1].c,0);
}
else
{
p[i][j][k]=p[i-1][j][k];
}
}
}
}
}
}
以上就是我最近一段时间内对动态规划算法的理解。