PS: This is a note.
动态规划(DP)
构成动态规划算法的三要素
动态规划对状态空间的遍历构成一张有向无环图,遍历顺序就是该有向无环图的一个拓扑序
- 有向无环图中的节点对应问题中的 “状态”
- 图中的边对应状态之间的 “转移”
- 转移的选取就是动态规划中的 “决策”
问题能用动态规划求解的三个基本条件
- 子问题重叠性————有些子问题会被重复计算多次,动态规划算法对每一个子问题只计算一次,然后将其计算结果保存下来,当再次需要计算已经计算过的子问题时,直接返回之前记录的结果,从而获得较高的效率
- 无后效性————动态规划要求已经求解的子问题不受后续阶段的影响,即每个状态都是过去历史的一个完整总结
- 最优子结构性质————问题的最优解,其所包含的子问题的解也是最优的
DP例题
e.g. 最少硬币找钱问题
e.g. 数字金子塔 II
e.g. 红牌(洛谷P1130)
e.g. 传球游戏(洛谷P1057)
e.g. 方格取数(洛谷P1004)
最长上升子序列(LIS)
LIS例题
e.g. 怪盗基德的滑翔翼(信息学奥赛一本通(C++版)在线评测系统 题号1286)
e.g. 登山(信息学奥赛一本通(C++版)在线评测系统 题号1283)
e.g. 友好城市(信息学奥赛一本通(C++版)在线评测系统 题号1263)
e.g. 最长上升子序列和(信息学奥赛一本通(C++版)在线评测系统 题号1285)
01背包
01背包公式: f[i][j]=max(f[i-1][j],f[i-1][j-v[i]]+w[i])
#include<bits/stdc++.h>
using namespace std;
int f[2][1010],v[1010],w[1010];
int n,m,p;
int main()
{
cin>>n>>m;
for(int i=1;i<=n;i++)
{
cin>>v[i]>>w[i];
}
for(int i=1;i<=n;i++)
{
//动规方程
for(int j=0;j<=m;j++)
{
f[1][j]=f[0][j];
if(j>=v[i])
{
f[i][j]=max(f[i-1][j],f[i-1][j-v[i]]+w[i]);
}
}
//优化(时间上无法优化,优化空间)
p^=1;
for(int j=0;j<=m;j++)
{
f[p][j]=f[p^1][j];
if(j>=v[i])
{
f[p][j]=max(f[0][j],f[0][j-v[i]]+w[i]);
}
}
}
cout<<f[1][m]<<endl;
cout<<f[p][m]<<endl;
}