一、算法的说明
动态规划主要针对优化问题。所谓规划就是“比较全面的长远的计划”。在动规的算法策略中,它体现在他的决策不是线性的,而是全面考虑各种不同的情况再分别进行决策,最后通过多阶段决策逐步找出问题的最终解。当各个阶段采取决策后,会不断决策出新的数据,直到找到最优解为止。每次的决策又依赖于当前的状态,又随即引起状态的转移。一个决策序列就是在变化的状态中产生出来的。所以,这种多阶段的最优化决策解决问题的过程称为动态规划。
二、算法的基本思想
动态规划算法通常用于求解具有某种最优性质的问题。在这类问题中,可能会有许多可行解。每一个解都对应于一个值,我们希望找到具有最优值的解。
动态规划算法与分治法类似,其基本思想也是将待求解问题分解成若干个子问题,先求解子问题,然后从这些子问题的解得到原问题的解。与分治法不同的是,适合于用动态规划求解的问题,经分解得到子问题往往不是互相独立的。若用分治法来解这类问题,则分解得到的子问题数目太多,有些子问题被重复计算了很多次。如果我们能够保存已解决的子问题的答案,而在需要时再找出已求得的答案,这样就可以避免大量的重复计算,节省时间。
我们可以用一个表来记录所有已解的子问题的答案。不管该子问题以后是否被用到,只要它被计算过,就将其结果填入表中。这就是动态规划法的基本思路。具体的动态规划算法多种多样,但它们具有相同的填表格式。
三、算法的设计基本步骤
- 确定问题的决策对象。
- 对决策过程划分阶段。
- 对各阶段确定状态变量(选择阶段)。
- 根据状态变量确定费用函数和目标函数。
- 建立各阶段状态变量的转移过程,确定状态转移方程。
四、算法的应用条件
- 满足最优化原理:又称最优子结构性质,是指一个问题的最优解包含其子问题的最优解,或一个最优化策略的子策略总是最优的。
- 具有无后向性:即某一阶段状态一旦确定,就不受这个状态以后决策的影响。也就是说,某状态以后的过程不会影响以前的状态,只与当前状态有关,这种特性被称为无后效性。
- 子问题重叠:也就是说子问题之间是不独立的,一个子问题在下一个阶段决策中可能被多次使用到。对有分解过程的问题还表现在自顶向下分解问题时,每次产生的子问题并不总是新问题,有些子问题会反复出现多次。这个性质并不是动态规划适用的必要条件,但是如果该性质无法满足,动规算法同其他算法相比就不具备任何优势。
五、算法的相关应用
0/1背包问题,完全背包问题,分组背包问题,二分查找树,数字三角形,最短路径,项目管理,活动优先次序问题、资源分配问题等等。
六、典型例题一:数塔问题
1.问题描述
有形如右图的一个数塔,从顶部出发,在每一结点可以选择向左走或是向右走,一直走到底层,要求找出一条路径,使路径上的数值和最大。
2.思路分析
🔲原始信息存储
层数用整型变量n存储;数塔中的数据用二维数组data[ ][ ]存储,下三角阵
🔲动态规划过程存储
必须用二维数组d[ ][ ]存储各阶段的决策结果。二维数组d的存储内容如下:
d[n][j]=data[n][j],其中j=1,2,……,n;(对第n行)
d[i][j]=max(d[i+1][j],d[i+1][j+1])+data[i][j],其中i=n-1,n-2,……1,j=1,2,……,i;
🔲最后d[1][1]存储的就是问题的最大值。
🔲可以通过分析d,得到路径。
🔲输出data[1][1]“9”;
b=d[1][1]-data[1][1]=59-9=50 b与d[2][1],d[2][2] 比较b与d[2][1]相等,输出data[2][1]“12”;
b=d[2][1]-data[2][1]=50-12=38 b与d[3][1],d[3][2] 比较b与d[3][1]相等,输出data[3][1]“10”;
b=a[3][1]-data[3][1]=38-10=28 b与d[4][1],d[4][2] 比较b与d[4][2]相等,输出data[4][2]“18”;
b=d[4][2]-data[4][2]=28-18=10 b与d[5][2],d[5][3] 比较b与d[5][3]相等,输出data[5][3]“10”
🔲为了设计简洁的算法,可以用三维数组a[50][50][3]存储以上确定的三个数组的信息。
a[50][50][1]代替数组data,
a[50][50][2]代替数组d,
a[50][50][3]记录解路径。
3.算法的伪码描述
for( i=1 ;i<=n;i++)
for (j=1;j<=i;j++){
input(a[i][j][1]);
a[i][j][2]=a[i][j][1];
a[i][j][3]=0;}
for (i=n-1 ; i>=1;i--)
for (j=1 ;j>= i ;j++)
if (a[i+1][j][2]>a[i+1][j+1][2]) {
a[i][j][2]=a[i][j][2]+a[i+1][j][2];
a[i][j][3]=0;}
else {
a[i][j][2]=a[i][j][2]+a[i+1][j+1][2];
a[i][j][3]=1;}
print('max=’,a[1][1][2]);
j=1;
for( i=1 ;i<= n-1;i++){
print(a[i][j][1],‘->’);
j=j+a[i][j][3]; }
print (a[n][j][1]);
七、典型例题二:资源分配问题
1.问题描述
现有7万元投资到A,B,C三个项目,利润见表,问题:求总利润最大的资源分配方案。
2.思路分析
🔲划分阶段或找到子问题;比较直观的划分:每个阶段考虑一个项目的投资;每个阶段增加一个项目,考察投资利润情况。
🔲选择状态;每个阶段可以得到的最大利润;在第i阶段:前i个项目通过投资组合可得的最大利润;投资额不定:第i阶段总投资额为x时,设fi(x)为最大利润。
🔲确定决策并写出状态转移方程;
f1(x)= g1(x)
fi(x)= max{gi(xi)+ fi-1(x-xi)}
gi(x):i项目投x后可得利润;
fi(x):第i阶段投x的最大利润;0≤x≤n,0≤xi≤x
3.算法的伪码描述
main( )
{
int i,j,k,m,n,rest;
int a[100][100],gain[100];
float q[100],f[100],temp[100];
print(“projects? ”);
input (m);
print(“money? ”);
input (n);
print(“input one item gain table:”);
for( j=0;j<= n;j++)
{
input(q[j]);
f[j]=q[j];
}
for( j=0;j<= n;j++)
a[1,j]=j;
for( k=2;k<=m;k++)
{
for( j=0;j<= n;j++)
{
temp[j] = q[j];
input(q[j]);
a[k][j]=0;
}
for( j=0 ;j<= n;j++)
for( i=0 ;i<=j;i++)
if (f[j-i] + q[i] > temp[j])
{
temp[j] = f[j-i]+q[i];
a[k,j] = i; }
for(j=0;j<= n;j++)
f[j] = temp[j];
}
rest=n;
for(i=m;i>=1;i--)
{
gain[i] = a[i][rest];
rest = rest - gain[i];
}
for(i=1;i<=m;i++)
print(gain[i],” ”);
print(f[n]);
}