动态规划
动态规划
这一篇完全写不完,只能把今天回顾的内容记录一遍,所以之后肯定会补充。
概念性知识(使用条件)
最优子结构
即:一个情形面前只有有限个抉择,那么要想让当前得到的结果最优,那么一定会去贪心地做出选择。
无后效性
把问题划分成阶段,那么按照逻辑顺序,当前阶段的决策不会受到之后所做的决策的影响。
可继承性
对于第
要素
阶段、状态、转移方程、初始条件、答案
基础问题 LIS 与 LCS
LIS 最长上升子序列
定义
很容易写出一个
所以我们考虑换个方式来定义
枚举每一个
Code
#include<bits/stdc++.h> using namespace std; const int N=1e5+10; int dp[N],a[N],n; int main() { int n; cin>>n; for(register int i=1;i<=n;++i)cin>>a[i]; memset(dp,0x3f,sizeof dp); int ans=-1; for(register int i=1;i<=n;++i) { int pos=lower_bound(dp+1,dp+n+1,a[i])-dp-1; if(pos+1>ans)ans=pos+1; dp[pos+1]=min(dp[pos+1],a[i]); } cout<<ans; return 0; }
LCS最长公共子序列问题
定义
当
否则,
这样做是
对于特殊情况,如 Luogu P1439,两个序列的元素实际上都相同(只是不对应相同),还有
Code-Brute Force Ver.
#include<bits/stdc++.h> using namespace std; int main() { int n; cin>>n; int a[n+10],b[n+10],dp[n+10][n+10]; for(register int i=1;i<=n;++i)cin>>a[i]; for(register int i=1;i<=n;++i)cin>>b[i]; for(register int i=0;i<=n;++i) for(register int j=0;j<=n;++j)dp[i][j]=0; for(register int i=1;i<=n;++i) { for(register int j=1;j<=n;++j) { dp[i][j]=max(dp[i-1][j],dp[i][j-1]); if(a[i]==b[j])dp[i][j]=max(dp[i][j],dp[i-1][j-1]+1); } } cout<<dp[n][n]; return 0; }
Code-Better Ver.
随便给定两个题意序列
我们发现,从第二个序列中选出一个子序列,其位置肯定是递增的,这两者是充要关系。
那么如果我们从第三个序列(也就是抽象过后的第一个序列)中选择一段递增的数字,那么其必定是序列一二的公共子序列;进一步地,我们选择第三个序列的LIS,那么其对应的就是序列一二的 LCS。
#include<bits/stdc++.h> using namespace std; const int N=1e6; int dp[N],a[N],b[N],idx[N]; int main() { int n; cin>>n; for(register int i=1;i<=n;++i) { cin>>a[i]; idx[a[i]]=i; } for(register int i=1;i<=n;++i) { cin>>b[i]; a[idx[b[i]]]=i; } int ans=-1; memset(dp,0x3f,sizeof dp); for(register int i=1;i<=n;++i) { int pos=lower_bound(dp+1,dp+i,a[i])-dp-1; if(pos+1>ans)ans=pos+1; dp[pos+1]=min(dp[pos+1],a[i]); } cout<<ans; return 0; }
记忆化搜索
比如用递归求解 Fibonacci 的时候,我们会经常访问同一个子问题
下面是记忆化求 FIbonacci 的示例:
int f[N] int get(int x) { if(f[x])return f[x]; if(x==1||x==2)return 1; return f[x]=get(x-1)+get(x-2); }
背包
https://www.cnblogs.com/Hanggoash/p/18561527
树形DP
节点选择与否
例题是 "没有上司的舞会",初步来说这种比较简单。
树上选链
例题是 CF2050G
题意是:给定一棵树,可以断掉从
核心思想是要想到:对于一个点,我们最优答案里选出来的这条链,对于某个点
这样就很容易想到定义
然后就很好转移了。
Code
树形背包
例题是 “选课”,大概就是还需要定义一维状态来记录当前子树之内选取了多少个物品。
算法流程大概是:对于当前节点
区间DP
例题大概是石子合并之类的,注意有一个 trick 在于:对于成环的问题可以拆成首尾相接的两条链来处理,这样就能很自然的判断出环上的情况,否则还需要加一层复杂度,多枚举一个断点。
本文来自博客园,作者:Hanggoash,转载请注明原文链接:https://www.cnblogs.com/Hanggoash/p/18443858
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效