动态规划(Dynamic Programming,DP)
动态规划和递归有一个非常相似的地方:自己不会就推卸给别人
下面是例题:
方格取数(一维)
题目描述
有一个长度为N数列,小明从第一个数开始,他每次可以走2步或3步,最终到达第N个数,求他经过所有数的总和的最大值。
输入格式
第一行是一个整数N
第2行,有N个以空格分隔的整数,表示第i个数
输出格式
一行一个整数,表示能得得到的最大值
输入样例
5 1 2 3 4 5
输出样例
9 数据范围
1<n<10000000
样例说明
1→3→5,1+3+5=9
对于上面这道题,第一反应应该是:DFS或BFS
但是仔细看数据范围,一百万显然会时间超限
这时,就需要用到动态规划
到第i个数的前一步,是不是有且仅有两条路:i-3和i-2
这里我们建一个数组f,f[i]表示走到i可以获得的最大价值
那对于第i个数,他只能是从i-2和i-3走过来的
那么我如果想要求得f[i]的最大值,是不是就需要得到f[i-2]或者是f[i-3]的最大值
那么f[i]=f[i-2]+a[i]//a[i]表示数列里第i个数
或者是f[i]=f[i-3]+a[i]
那么我有两条路,我想要获得最大值,我是不是就要取上面两条路能获得的最大值
所以最后:
f[i]=max(f[i-2]+a[i],f[i-3]+a[i])
最后只需要遍历1~n的最大值,输出a[n],时间复杂度为O(n),可以通过此题
完整代码:
#include <bits/stdc++.h>
using namespace std;
const int N = 1000001;
int n;
int a[N];
int f[N];
int main() {
cin >> n;
for (int i = 1; i <= n; i++) {
cin >> a[i];
}
f[1] = a[1];
for (int i = 3; i <= n; i++) {
if (i == 3) {
f[i] = f[i - 2] + a[i]; //特殊处理
} else {
f[i] = max(f[i - 2] + a[i], f[i - 3] + a[i]);
}
}
cout << f[n] << endl;
return 0;
}
再来仔细看下求动态规划的过程
首先,确定了f[i]表示走到i可以获得的最大价值,这就叫做状态,找好状态是做出动态规划的关键,状态一般和题目答案相关,或者就是答案本身,如这道题,f[n]就是答案本身
然后我们找到了和f[i]相关的子问题f[i-2],f[i-3],这叫做阶段
最终我们确定:f[i]=max(f[i-2]+a[i],f[i-3]+a[i]),这就是决策,本质上叫做状态转移方程,本质上就是推卸责任
使用动态规划必须满足无后效性和最优子结构
在上面这个问题中,f[i]的结果对于f[i-2],f[i-3]没有影响,因为我们是从前往后走的,这就是无后效性
f[i]是最优的,f[i-2]和f[i-3]同样是最优的,这就是最优子结构
建议尝试:#6255. 拦截导弹