dp by zhx
dp是什么
动态规划,三要素:状态、转移,初始化。状态是最基础的,转移是状态之间的关系,初始化是状态的边界,如何设计状态。
引入-1.-1 P1216 [IOI1994]数字三角形
给一个数字三角形,可以向下或向右下走,试问路径数字和的最大值。
状态:\(f_{i,j}\) 表示在 \((i,j)\) 时的最大权值和。为什么是这个状态?在走过程中,位置和权值在变化,求价值,所以状态的形式是 \(\texttt{f[位置]=价值}\),把要求的放到右边,其它的放左边。
-1.-1.5 数字三角形2
若将路径数字和的最大值变为路径数字和 \(\bmod 100\) 的最大值?原状态失效,不满足最优子结构。
怎么做?我们可以加维度!设置三维状态,把右边变化的量加进去!\(f_{i,j,k}\) 为在 \((i,j)\) ,和\(\mod 100=k\) 是否可能。
代码:
#include <bits/stdc++.h>
#define int long long
const int MAXN=105;
int a[MAXN][MAXN],dp[MAXN][MAXN][MAXN];
int n;
signed main(){
std::cin>>n;
for(int i=1;i<=n;i++){
for(int j=1;j<=i;j++){
std::cin>>a[i][j];
}
}
dp[1][1][a[1][1]%100]=1;
for(int i=1;i<=n;i++){
for(int j=1;j<=i;j++){
for(int k=0;k<=99;k++){
if(dp[i-1][j][k]||dp[i-1][j-1][k])dp[i][j][(k+a[i][j])%100]=1;
}
}
}
int ans=-1;
for(int i=1;i<=n;i++){
for(int j=0;j<=99;j++){
if(dp[n][i][j]){
ans=std::max(ans,j);
}
}
}std::cout<<ans<<'\n';
return 0;
}
-1.-2 斐波那契数列
\(f_0=0,f_1=1,f_i=f_{i-1}+f_{i-2}\)。
给定 \(n\),求 \(f_n\bmod (10^9+7),n\leq 100\)
1.用定义推
f[0]=0,f[1]=1;
for(int i=1;i<=n;i++)f[i]=(f[i-1]+f[i-2])%mod;
2.用自己更新别人
f[0]=0,f[1]=1;
for(int i=0;i<=n;i++)f[i+1]+=f[i],f[i+2]+=f[i];
3.记忆化搜索
我们先来爆搜
int dfs(int x){
if(n==0)return 0;
if(n==1)return 0;
f[x]=(f(x-1)+f(x-2));
return f[x];
}
这个东西是 \(O(f_n)\) 的,不可以!
记忆化!
若我们已经求出了一个值,就不需要再求一遍!
int dfs(int x){
if(n==0)return 0;
if(n==1)return 0;
if(g[x])return f[x];
g[x]=1;
f[x]=(f(x-1)+f(x-2));
return f[x];
}
比较简单。
有些 dp 只能用第一种,有些只能用第二种,如现有一个转移 $$f_i=(\max_{l_i\leq j\leq r_i}f_j)+a_i$$,注意到其中有区间求和的形式,我们可以使用第一种方法,大力线段树,做到\(O(n \log n)\)。
但是如果自己可以快速转移到区间,那么就用第二种。
这是ds优化dp的内容。
记忆化搜索一般在博弈论dp中用到。
dp分类
- 背包dp
- 区间dp
- 树形dp
- 数位dp
- 状压dp
- 插头dp
- 排列dp
- 博弈dp
- 一般dp
一般dp 例题
posted on 2024-05-27 21:04 ScapeGoatTree 阅读(6) 评论(0) 编辑 收藏 举报