dp学习笔记合集

待补充,每晚填坑。

可能会有数据结构优化,本文肯定不会咕咕。

写的时候会隐藏掉;

可催更,当然也一定不会有人来催更的吧

尽量不咕,可能每天都更;

dp分为分为 基本(特殊类型)dp问题和数据结构优化dp,可以在这里选择目录然后来看,不然可能有点乱。

本文默认各位已经掌握基本dp问题

特殊类型 dp:

区间dp

区间 dp 一般都是在一个指定的序列上的一些操作,

我们借助一下P1775来理解一下区间 dp:

这一道题目就是在一个序列上的操作,

当前区间的答案可以通过枚举断电的方式由两段相邻的子区间的答案合并而来

也就是不停地合并区间;

因此,实现过程就是:

枚举区间长度一层循环,枚举左端点一层循环,枚举断点一层循环,

然后令dp[i][j]表示[i,j]的最小值;

时间复杂度:\(O(n^3)\)

难点:推出柿子,枚举的时候需要不重不漏;

数位 dp

其实就是一位一位来做 dp 问题,因此,数位 dp 常用于一些于数位有关的问题,比如计数问题;

我们还是借助题目讲一下:P2602

我们一般会用记忆化搜索来做;

比如这道题目要我们求1~99内出现每个数位的个数,那么我们肯定会递归下去

递归的时候,会遍历所有的范围内的值,和其他类型的递归同理,想要更快就合并

比如有些题中某两个答案一样,那么就可以把这两种状态进行合并;

数位 dp 的时间复杂度通常和数位有关;

但是上面那题要处理一下前导零,也就是一个数的前面的 0 的个数,

我们都知道通过计算机筛选的时候会出现 0123101 这个数

这个数中,前面有一个多余的 0,显然是不能计算的,

如果这个数位是 0,并且它的前面也都是 0,那么就证明它是一个前导零;

然后此题是 10进制的,其实 2 进制什么的都是可以用数位 dp 的,比如CF276D

具体做法就不给了

因为数位 dp 基本都是套板子的,因此给一个板子(不是自己写的,转载,注明出处

ll dfs(int pos,int pre,int st,……,int lead,int limit)//记搜
{
    if(pos>len) return st;//剪枝
    if((dp[pos][pre][st]……[……]!=-1&&(!limit)&&(!lead))) return dp[pos][pre][st]……[……];//记录当前值
    ll ret=0;//暂时记录当前方案数
    int res=limit?a[len-pos+1]:9;//res当前位能取到的最大值
    for(int i=0;i<=res;i++)
    {
        //有前导0并且当前位也是前导0
        if((!i)&&lead) ret+=dfs(……,……,……,i==res&&limit);
        //有前导0但当前位不是前导0,当前位就是最高位
        else if(i&&lead) ret+=dfs(……,……,……,i==res&&limit); 
        else if(根据题意而定的判断) ret+=dfs(……,……,……,i==res&&limit);
    }
    if(!limit&&!lead) dp[pos][pre][st]……[……]=ret;//当前状态方案数记录
    return ret;
}
ll part(ll x)//把数按位拆分
{
    len=0;
    while(x) a[++len]=x%10,x/=10;
    memset(dp,-1,sizeof dp);//初始化-1(因为有可能某些情况下的方案数是0)
    return dfs(……,……,……,……);//进入记搜
}
int main()
{
    scanf("%d",&T);
    while(T--)
    {
        scanf("%lld%lld",&l,&r);
        if(l) printf("%lld",part(r)-part(l-1));//[l,r](l!=0)
        else printf("%lld",part(r)-part(l));//从0开始要特判
    }
    return 0;
}

排列 dp

(由于这个是作者本人近几天刚刚学的,有学术错误请指出)

这应该是一种非常罕见的 dp 种类,

题目也并不是很多,窝凭借自己的力量是没有找到,最后感谢王学逸等大佬提了一些题目,这里先放一下P7406,还是比较经典的题

这一道题中 dp 的对象的是 1~n 的排列

一般情况下,都用 \(dp_i\) 来表示1~i的排列,同理,这道题目可用 \(dp_i\) 来表示前 i 个数合法的最小值

这道题是要求让我们临项交换,其实也就是把这个序列分为了很多段,进行逆序对

其他题目的转移一般也会是从小到大或者从大到小;

具体做法就先不说了,感觉里面题解的内容都能讲的比我更清楚一些

树形 dp

树形 dp 就是在树上进行的 dp,

我们拿一道板子题来练练手P1352

那么,我们可以看到的是,这些员工的关系可以组成一棵树,最开始是地位高的,依次往下

一般的树形 dp 都是由上向下搜索,因此,我们这里就这样,选用上司的心情来进行状态转移方程。

上司来——最好心情 = 上司心情 + 每个直接下属不来的最好心情值;
上司不来——最好心情 = 每个直接下属来与不来的最好心情值的优值;

发现了吗,整个过程其实就是一个从上向下的 dfs

但是光那样做会被卡掉

因此,我们还需要一点时间方面的优化,

想上文写的一样,大多数 dp 优化都在于合并,那么,在树形 dp 中也要合并

恰当的合并子节点的答案

时间复杂度:\(O(n)\)

推荐易上手题目:Link

核心代码

posted @ 2022-05-27 09:55  zsdqwq  阅读(46)  评论(2编辑  收藏  举报