DP从入门到入土

DAGDP

食物链
简单的拓扑DP,转移方程通式 \(f[v]+=f[u],rd[v]--\)
犯错

for (int i=head[u];i;i=e[i].nxt)
        {
            int v=e[i].v;
            f[v]+=f[u];
            rd[v]--;
            if (!rd[v]) q.push(v);
        }
/*6,v写成i*/
for (int i=1;i<=n;i++)
        if (!rd[i]) 
        {
            if (cd[i]) f[i]=1;
            q.push(i);
        }
/*特殊情况的处理,入队的对象*/

Code

区间DP

P3205 [HNOI2010]合唱队
这是一种区间DP的典型模板,左右操作,求方案数
对于这类问题,可以分别表示左右两种操作
\(f[i][j]\) 表示在区间 \([i,j],i\) 从左边插入
\(g[i][j]\) 表示在区间 \([i,j],j\) 从右边插入
两个dp数组自然拥有独特的两个阶段

  • 对于 \(f\): 相对于上一区间,要么是从 \(i+1\) 比较,再加入,或者上一次加进去的是 \(j\) ,则阶段为\(f[i+1][j],g[i+1][j]\),
    注意这是上一阶段,换句话说就是该阶段的 \(i\)是被加入的,也就是上一阶段是不存在 \(g[i][j]\)这中转移状态
  • 对于 \(g\): 同理可耻,要么是上一次加入的是 \(i\),也就是加到左边,即 \(f[i][j-1]\),要么就是上一次加进去的是 \(j-1\) ,即 \(g[i][j-1]\);
    注意上一阶段,不存在 \(j\) 的情况
    错误 转移方程式抄错
for (int len=2;len<=n;len++)
        for (int i=1;i+len-1<=n;i++)
        {
            int j=i+len-1;
            f[i][j]=(f[i+1][j]*(a[i+1]>a[i])+g[i+1][j]*(a[i]<a[j]))%mod;
            g[i][j]=(f[i][j-1]*(a[j]>a[i])+g[i][j-1]*(a[j]>a[j-1]))%mod;
        }
/*6,抄错,炒成:g[i][j]=(g[i][j-1]*(a[j]>a[i])+g[i][j-1]*(a[j]>a[j-1])%mod/

总结 此类区间DP,转移的方向明确,要么上一阶段的左边要么右边,不需要区间枚举
Code

序列DP

粉刷匠
序列DP
对于每一行 \(f[i][j]=max(f[k][j-1]+max(蓝 红,[k,i])\)
对于每一行 \(dp[i][j]\)\(i\)表示行号,
\(dp[i][j]=max(dp[i][j-k]+f[m][k]);\)
答案 \(dp[n][T]\);
注意 处理前缀和优化时出现了错误

for (int k=0;k<l;k++)
                {
//                    int s1=a[i][l]-a[i][k];
//                    int s2=b[i][l]-b[i][k];
                    int js=sum[i][l]-sum[i][k];
                    f[l][r]=max(f[l][r],f[k][r-1]+max(js,l-js-k));
                }

Code

树形DP

小齐挖矿
对于一个子节点,距离最近的要么就是父亲,要么就是父亲去的仓库

Code

女仆咖啡厅桌游吧
tmd 燃起来了! 有点饲喂量的树形DP

Code

P7103 「C.E.L.U-01」族谱树

  • 暴力做法 求每个点的 \(LCA\)直接询问,不过会超时

  • 树形DP
    首先 \(LCA\)的一种性质,如果以 \(i\) 为节点的子树具有相同的最近公公祖先 \(i\),换句话说,若果这个点是某两个点的LCA,那么他的父亲必然是公公祖先

就这一性质,我们假设 \(u\) 即节点是第 \(k\) 层节点的最近公共祖先,那么就看看他的儿子 \(v\)是不是 \(k+1\)的了,

但是要 \(v\)成为深度 \(k+1\) 的LCA,必须满足子树 \(v\) 的最大深度超过 \(k+1\),故我们需要预处理出每个节点的最大深度

还有一种情况,如果 \(u\) 的儿子有多个,假若存在两个以上的 \(v_1,v_2..\) 满足 \(f(v)>=k+1\) (\(f[v]\)表示该点的最大深度),那么他们都不可以成为LCA

Code

P5658 [CSP-S2019] 括号树

P4438 [HNOI/AHOI2018]道路
记忆化搜索,树形思想,公式迷惑
P2899 [USACO08JAN]Cell Phone Network G
水题
P2458 [SDOI2006]保安站岗
水题

换根DP

P3478 [POI2008]STA-Station
换根式子:\(f[v]=-size[v]+f[u]+(n-size[v])\)
用人话讲就是:根有 \(u\)\(v\)时,子树 \(u\) 全体深度+1(不含子树\(v\)),子树 \(v\)全体深度-1

P2986 [USACO10MAR]Great Cow Gathering G

换根水题
\(ans\)记得开大点,否则60
转移式子:\(dp[v]=dp[u]-size[v]\times e[i].w+(sum-size[v])\times e[i].w\)

背包DP

P3188 [HNOI2007]梦幻岛宝珠

关于二进制神奇做法的背包,感觉是在每一位二进制上进行背包

两个转移点

  • 同级转移,我们有
    \(f[i][j]=max\{f[i][j-a[i]]+val[i]\}\)
  • 高位转低位我们有
    \(f[i][j]=max\{f[i][j-k]+f[i-1][min(1000,(k<<1)|((w\&(1<<(b-1)))!=0)]\}\)

位运算

P3423 [POI2005]BAN-Bank Notes

二进制拆分,注意数组越界(拆分写挂,变成负数)

Code

[P1782](https://www.luogu.com.cn/problem/P1782)

典型的旅行商问题,多重背包里套完全背包,我用的是二进制拆分(真好用虽然60,这里建议直接吸氧!

Code

期望DP

P1654 OSU!

Code

装压DP

讲真的,装压是我见过最优美的DP题目

P1433 吃奶酪

\(f[i][s]\) 表示起点为 \(i\),完成状态为 \(s\) 的最小距离

转移有 \(f[i][s]=min\{f[j][s-(1<<(i-1)]+dis(i,j)\}\)

优美之处在于:距离不是限制矩阵的大小,只是运算的工具,通过位运算进行判重 if(!(s&(1<<(i-1)),转移也非常优美,更新新的起点,并在二进制状态下更新,beautiftying

[USACO12MAR]Cows in a Skyscraper G

这题让初次学习的我知道了初始化,通过判断该点是否在状态之中,可谓妙哉!if((j)&(1<<k))

\(f[i][j]\) 表示有i个层时,满状态下的最小体积书
最后用过倒叙判断是否为inf即可

P3226 [HNOI2012]集合选数
一道构造好题!

单调队列优化

P1823 [COI2007] Patrik 音乐会的等待

posted @ 2021-03-13 08:10  zxsoul  阅读(120)  评论(3编辑  收藏  举报