动态规划入门
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了动态规划入门相关的知识,希望对你有一定的参考价值。
动态规划入门
什么是动态规划?
动态规划(dynamic programming)是求解决策过程(decision process)最优化的数学方法。把多阶段过程转化为一系列单阶段问题,利用各阶段之间的关系,逐个求解,创立了解决这类过程优化问题的新方法——动态规划。
动态规划可以分为几类:
线性动规:拦截导弹,合唱队形,挖地雷,建学校,剑客决斗等;
区域动规:石子合并, 加分二叉树,统计单词个数,炮兵布阵等;
树形动规:贪吃的九头龙,二分查找树,聚会的欢乐,数字三角形等;
给定k阶段状态变量x(k)的值后,如果这一阶段的决策变量一经确定,第k+1阶段的状态变量x(k+1)也就完全确定,即x(k+1)的值随x(k)和第k阶段的决策u(k)的值变化而变化,那么可以把这一关系看成(x(k),u(k))与x(k+1)确定的对应关系,用x(k+1)=Tk(x(k),u(k))表示。这是从k阶段到k+1阶段的状态转移规律,称为状态转移方程。
题目描述
观察下面的数字金字塔。
写一个程序来查找从最高点到底部任意处结束的路径,使路径经过数字的和最大。每一步可以走到左下方的点也可以到达右下方的点。
7
3 8
8 1 0
2 7 4 4
4 5 2 6 5
在上面的样例中,从7 到 3 到 8 到 7 到 5 的路径产生了最大
输入输出格式
输入格式:
第一个行包含 R(1<= R<=1000) ,表示行的数目。
后面每行为这个数字金字塔特定行包含的整数。
所有的被供应的整数是非负的且不大于100。
输出格式:
单独的一行,包含那个可能得到的最大的和。
输入输出样例
5
7
3 8
8 1 0
2 7 4 4
4 5 2 6 5
30
说明
题目翻译来自NOCOW。
USACO Training Section 1.5
这个题可以算是动态规划的开山鼻祖了,如果我们不知道dp这种东西,那你会怎么做?
方法一:爆搜
我们可以爆搜,搜索从顶向下的每一条路径
#include<cstdio>
using namespace std;
const int maxn = 1010;
int r,a[maxn][maxn];
int max(int x,int y) {
return x > y ? x : y;
}
inline void input() {
scanf("%d",&r);
for(int i = 0; i < r; i++)
for(int j = 0; j <= i; j++)
scanf("%d",&a[i][j]);
return;
}
inline int maxnumber(int i,int j) {
if (i == r)
return a[i][j];
return max(maxnumber(i + 1,j),maxnumber(i + 1,j + 1)) + a[i][j];
}
int main() {
input();
printf("%d",maxnumber(0,0));
return 0;
}
这样一来我们的搜索总数达到了2^(d-1),其中d是三角形总层数
肯定是不可取的
方法二:记忆化搜索
定义一个数组vis,表示该数据有没有被计算过,如果被计算过,那就直接return;没有计算过就计算。
#include<cstdio>
#include<cstring>
using namespace std;
const int maxn = 1010;
int r,a[maxn][maxn];
int vis[maxn][maxn];
int max(int x,int y) {
return x > y ? x : y;
}
int maxnumber(int i,int j) {
if (vis[i][j] == -1) {
if (i == r)
vis[i][j] = a[i][j];
else
vis[i][j] = max(maxnumber(i + 1,j),maxnumber(i + 1,j + 1)) + a[i][j];
}
return vis[i][j];
}
inline void input() {
scanf("%d",&r);
memset(vis,-1,sizeof(vis));
for(int i = 0; i < r; i++)
for(int j = 0; j <= i; j++)
scanf("%d",&a[i][j]);
maxnumber(0,0);
}
int main() {
input();
printf("%d",vis[0][0]);
return 0;
}
时间复杂度降成了O(n) !
方法三:动态规划
动态规划问题需要设置状态(一维或多维数组)和转移方程
还是以数字三角形问题为例
我们设f[i][j]表示第i行第j列的点走到底层的最优答案。
这时的f[i][j]就是状态
f[i][j] = a[i][j] + max(f[i+1][j], f[i+1][j+1])
由子问题推导原问题的转移式就是状态转移方程
#include<cstdio>
using namespace std;
const int MAXN = 1010;
int r,a[MAXN][MAXN],f[MAXN][MAXN];
int max(int x,int y) {
return x > y ? x : y;
}
int main() {
scanf("%d",&r);
for (int i = 1; i <= r; i++)
for (int j = 1; j <= i; j++) {
scanf("%d",&a[i][j]);
f[i][j] = a[i][j];
}
for (int i = r - 1; i > 0; i--)
for (int j = 1; j <= i; j++)
f[i][j] += max(f[i + 1][j],f[i + 1][j + 1]);
printf("%d",f[1][1]);
return 0;
}
dp代码相对来说要更简洁,但思维难度较高。
入门篇就讲到这里!
(完)
以上是关于动态规划入门的主要内容,如果未能解决你的问题,请参考以下文章