ScapeGoatTree

Pas选手的尊严

导航

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  阅读(3)  评论(0编辑  收藏  举报