贪心法,是一种在每一部选择中都采取在当前状态下最好或最优的选择,从而获得最优解的算法。一种直观的解释就是『一直向前!』。
贪心法可以解决一部分常见算法问题,比如最小生成树、哈弗曼编码等,并且如果一个问题可以用贪心法解决的话,贪心法一定是最优的算法。
对于大部分问题如果使用贪心法未必会得到最优解。比如走迷宫的时候一直往终点走,就很有可能走到死胡同中。
不过对于大部分问题,贪心法至少可以给出一个接近最优的解,因而贪心法时常会用作辅助算法(比如用于确定搜索上界)或一些不需要给出精确最优解的情景。
动态规划是一种通过将原问题分解为相对简单的子问题的方式求解复杂问题的方法。动态规划的求解方式大多是递推,也会有一些问题必须用递归求解,通常首先确定问题的状态表示,再推出状态之间的转移方程,从而求出最终状态的解。
动态规划能正确解决问题的条件有三个:
最优子结构:局部最优解能够决定全局最优解(通常需要多个局部最优来决定全局解);
无后效性:子问题的解一旦确定,就不会再因为其它子问题的求解而改变;
重叠子问题:对于必须要用递归求解时,每次产生的子问题并不总是新问题,有些子问题会被重复计算多次,通过记录子问题的解而提升效率。
动态规划有很多类型,按照动态规划的顺序划分有线性动态规划和树形动态规划。此外带状态压缩的动态规划也是经常出现在程序设计竞赛中的知识点。
动态规划的经典问题有很多,诸如最长公共子序列(LCS),最长上升子序列(LIS),背包问题(01背包,多重背包等),连通性动态规划(俗称插头DP),哈密顿路,斯坦纳树等,感兴趣的读者可以通过网络搜索相关资料继续阅读。
数塔问题
不适合贪心策略
动态规划,可理解为递推,由上一层两个值中选择一个较大的,
计算出本层
#include <iostream>
#include <cstdio>
using namespace std;
const int N = 100;
// 下面这个函数实现的是更新最大值,o赋值为o和x的最大值
template <class T>
void updateMax(T& o, const T& x) {
o = (o > x) ? o : x;
}
// f数组为动态规划的状态数组
// num数组为读入的数塔
// n为读入的数塔高度
int f[N][N], num[N][N], n;
int main() {
// 读入n和数塔数组num
scanf("%d", &n);
for (int i = 1; i <= n; ++i) {
for (int j = 1; j <= i; ++j) {
scanf("%d", &num[i][j]);
}
}
// step 1 begin: 在这里实现动态规划算法逻辑
//从顶层开始循环,逐层更新最大值
for(int i=1;i<=n;++i){
for(int j=1;j<=i;++j){
//f[i][j]保存的是从顶部到(i,j)的数值和最大值
updateMax(f[i][j],max(f[i-1][j],f[i-1][j-1])+num[i][j]);
}
}
// step 1 end.
// 定义最终结果变量result,因为是计算最大值,所以初始化为0
int result = 0;
for (int i = 1; i <= n; ++i) {
// step 2 begin: 在这里实现更新最终结果的逻辑
//底层中,找到最大值作为result
updateMax(result,f[n][i]);
// step 2 end.
}
// 输出最终最大权值和result
printf("%d\n", result);
return 0;
}