概念&特点
1.多阶段决策:问题求解分多个阶段。某状态决策受之前影响且对之后有影响。
2.最优性原理(最优子结构):各阶段类似,可递推,存在最优决策序列,且其中任意决策均为局部最优。
3.枚举-存在大量重复:由于状态转移方程描述的是问题的递推性质,部分会产生大量重复(优化:记忆化搜索)/正因dp枚举了所有可能最优决策,故相较于贪心的以一个贪心策略为主标准的算法更有可能求得最优解。(与贪心问题最大的区别)
处理方法
分阶段(在设计算法时),目的是将大问题转化小问题,划分标准是区分各状态的关联(eg.时空间或逻辑上从前到后/从后到前之一)(注意关联不一定是相邻状态之间的)
状态变量记录状态(常用数组,特别注意数组的意义。这在记忆化处理中显得尤为重要)
状态转移方程描述决策,体现递推过程,通过遍历枚举各种情况
边界条件是递推的边界(起点或终点),在进行递推前就应该明确
递推用于实现状态转移方程(决策)的遍历枚举,看情况决定正推(eg.只能走右走下)还是反推(数塔),哪个简单用哪个
实在不行就递归,不过要考虑栈深问题,容易tle...
常见问题
1.求最优解(最常见)
2.求方案数
3.判断某状态是否可能存在
两道裸题
最长上升子列
//最长上升子序列,正向递推,未经记忆化处理
#include<cstdio>
using namespace std;
int d[1010];
int maxlen[1010];
int main()
{
int i,j,N;
scanf("%d",&N);
for(i=0;i<n;i++)
{
int nTmp=0;
for(j=1;j<i;j++)
{
if(nTmp<maxlen[j])
nTmp=maxlen[j];
}
maxlen[i]=maxlen[j]+1;
}
int Max=-1;
for(i=0;i<n;i++)
{
if(Max<maxlen[i])
Max=maxlen[i];
}
printf("%d\n",Max);
return 0;
}
数塔
//数塔问题[记忆化搜索]
//思路:从最后一层开始向前动态规划,记录初值,记忆状态,注意可以看做是一个直角三角形,故边界条件为j<i
#include<cstdio>
#include<algorithm>
using namespace std;
const int M=1010;
int data[M][M];
int f[M][M];
int max(int x,int y) {return x>y?x:y;}
int main()
{
int i,j,n;
while(scanf("%d",&n)&&n)
{
for(i=1;i<=n;i++)
for(j=1;j<=i;j++)
scanf("%d",&data[i][j]);
for(j=1;j<=n;j++)
f[n][j]=data[n][j];
for(i=n-1;i>0;i--)
for(j=1;j<=i;j++)
f[i][j]=data[i][j]+max(f[i+1][j],f[i+1][j+1]);
printf("%d\n",f[1][1]);
}
}