树形DP和状压DP和背包DP

树形\(DP\)和状压\(DP\)虽然在\(NOIp\)中考的不多,但是仍然是一个比较常用的算法,因此学好这两个\(DP\)也是很重要的。而背包\(DP\)虽然以前考的次数挺多的,但是现在基本上已经成了人人都能AK的题了,所以也不经常考了。

树形DP

树形DP这个非常特殊,他好像和是唯一一个用深搜实现的DP,所以我们学好它也是应该的,其特点是通过深搜。

思路

  • 先找到一个根节点,然后预处理出所有子树的大小。
  • 然后深搜把最底层的子节点得状态处理出来。
  • 递归回溯到根节点,在回溯的时候完成状态转移。最后输出根节点的值。

\(Example\)

\(LuoguP2014\)(选课)这个题,就是一个比较典型的树形DP,顺便还考察了一下分组背包,

首先我们分析一下状态我们可以设\(dp[i][j]\)表示以\(i\)的子树中选\(j\)个(包括\(i\)自己)所得到的最大学分,因此我们可以采用树形\(DP\)的方法。

首先预处理出\(dp[i][1]\)表示只选\(i\)自己所得到的学分,然后我们可以进行状态转移,因为题目给的输入满足\(0\)一定是唯一的根节点,所以最后我们只要输出\(dp[0][m]\)首先我们要得到状态转移方程,然后仔细,细心的考虑边界条件,这也是一般DP的思路,首先我们可以得出方程:

\(dp[i][j] = max(dp[i][t] + dp[son(i)][j - t])(j\in[1, ~m],t\in [1,j]]且son(i)要全枚举一遍)\),此时我们还要处理比较棘手的问题,就是转移的顺序,

首先这是一个01背包所以我们要\(for(j = m; j >= 1; j--)\)而t的范围也是一个坑点,因为在推到t时,我们首先要满足比t小的一定要枚举出来,所以我们就要\(for(t = 1; t <= j; t++)\)

这个题基本上就解决了,代码:

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <vector>
using namespace std;
vector <int> son[100010];
int s[100010], out_degree[100010], in_degree[100010];
int dp[1000][1000];//表示i的子树选j个课程
int n, m;
inline void dfs(int a)
{
for (int i = 0; i < son[a].size(); i++)
{
int to = son[a][i];
dfs(to);
for (int j = m + 1; j >= 1; j--) //总共选m个加上0,就是m + 1个
for (int t = j; t >= 1; t--)
dp[a][j] = max(dp[a][j], dp[a][j - t] + dp[to][t]);
}
}
int main()
{
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i++)
{
int a, b;
scanf("%d%d", &a, &b);
son[a].push_back(i);
dp[i][1] = b;
dp[i][0] = 0;
}
dfs(0);
printf("%d", dp[0][m + 1]);
}
/*
7 4
2 2
0 1
0 4
2 1
7 1
7 6
2 2
*/

状压DP

状压\(DP\)就更神奇了,可以说是最有\(OI\)特点的一个\(DP\),因为它用到了位运算和二进制。因此像那些一个区间只有选或不选的操作的那些状态可以用二进制表示,然后在用一些不同寻常的东西,例如左移右移使可以状态转移。而判断是否是状压DP时可以查看数据范围,如果在\(20\)以内就可以使用状压\(DP\)。

思路

  • 跟其他的\(DP\)一样,我们首先还是要寻找状态,但是这个状态可能比较难搞,所以我们就需要压缩一下,也可以删除一些没有用的东西,且需要满足这些状态都很相似,且数量很多,此时就可以把它当成状态了。(总的来说,是先压缩状态,然后寻找合理状态)

  • 在压缩的时候需要运用位运算的一些操作

  • 当然一些运算的顺序也要记清楚,防止调试时间太长还找不到什么错误,

    位反(~ ) > 算术 > 位左移、位右移 > 关系运算 > 与 > 或 > 异或 > 逻辑运算

\(Example\)

\(LuoguP1879\)也是一个经典题,也经常被拿来用作写状压DP的入门题,当然愤怒的小鸟也是一个只要写过几个状压DP的就都能写出来的算法,因此也可以做一做,

我们分析一下玉米田这道题,题目让我们求总共有多少种方案数,而且数据范围还很小,这就在暗示我们采用状压\(DP\)的方法和套路。

预处理

首先应该压缩状态,且基本上状压\(DP\)压缩都是一样的方法

压缩状态+预处理代码:

for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++)
scanf("%d", &flag[i][j]);//判断土地是否肥沃
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++)
now[i] = (now[i] << 1) + flag[i][j];//压缩状态,状压DP的一般思路

寻找合理状态

题目要求要满足三个条件,即左右不能相邻,上下不能相邻,土地不能荒芜,为了方便,我们先使其满足左右不能相邻。

满足这个条件的前提是

(!(\(i\) & (\(i\) <<1))) && (!(\(i\) & (\(i\)>>1)))​

就是该状态左移和右移一位并与该状态\(and\)的结果是零,即并没有左移一位后的某一位与左移一位前的某一位相同,因为如果相同的话,就说明左右相邻

寻找合理状态代码:

for (int i = 0; i < (1 << m); i++)
if ( (!(i & (i << 1))) && (!(i & (i >> 1))) )
check[i] = 1;//说明此状态可行

状态转移

以上都是一行的预处理,现在我们要跨过这个界限,开始多行的转移了,在进行多行的转移的时候,就需要判断第二个条件,就是上下之间不能有相邻的状态。

满足这个条件的前提是

! (\(last\) & \(now\))

还需要判断第三个条件,不能有土地是荒芜的,这个也很好判断,因为我们已经预处理出每一行的最难的满足条件\(now[i]\), 如果(一个状态 & \(now[i]\)) == 该状态,说明此状态一定满足不荒芜,到此所有的条件都已经分析完毕,如果不懂可以手糊。

那就可以状态转移了,状压就成为了普通的二维DP了。

状态转移代码:

for (int i = 1; i <= n; i++)
for (int j = 0; j < (1 << m); j++)
if (check[j] && (now[i] & j) == j)//判断此状态可不可行,且不能有荒芜的土地,
for (int k = 0; k < (1 << m); k++)
if (!(k & j) && check[k])//上下不能有相邻的地方,比如如果上:10010,下:01100,那他们and的结果就不为0
dp[i][j] = (dp[i][j] + dp[i - 1][k]) % mod;

树形DP和状压DP和背包DP的更多相关文章

  1. dp乱写1:状态压缩dp(状压dp)炮兵阵地

    https://www.luogu.org/problem/show?pid=2704 题意: 炮兵在地图上的摆放位子只能在平地('P') 炮兵可以攻击上下左右各两格的格子: 而高原('H')上炮兵能 ...

  2. poj2411 Mondriaan's Dream (轮廓线dp、状压dp)

    Mondriaan's Dream Time Limit: 3000MS   Memory Limit: 65536K Total Submissions: 17203   Accepted: 991 ...

  3. BZOJ 4042 Luogu P4757 [CERC2014]Parades (树形DP、状压DP)

    题目链接 (BZOJ) https://www.lydsy.com/JudgeOnline/problem.php?id=4042 (Luogu) https://www.luogu.org/prob ...

  4. dp,状压dp等 一些总结

    也就作业几题而已,分析一下提醒 最重要的就是,记住,没用的状态无论怎么转移最后都会是没用的状态,所以每次转移以后的有值的状态都是有用的状态. 几种思考方向: 第一种:枚举当前的状态,转移成另外一个状态 ...

  5. 【BZOJ 4007】[JLOI2015]战争调度 DP+搜索+状压

    又是一道思路清新的小清晰. 观察题目,如果我们确定了平民或者贵族的任意一方,我们便可以贪心的求出另一方,至此20分:我们发现层数十分小,那么我们就也是状压层数,用lca转移,线性dp,至此50分(好像 ...

  6. 【洛谷4045】[JSOI2009] 密码(状压+AC自动机上DP)

    点此看题面 大致题意: 给你\(n\)个字符串,问你有多少个长度为\(L\)的字符串,使得这些字符串都是它的子串.若个数不大于\(42\),按字典序输出所有方案. 状压 显然,由于\(n\)很小,我们 ...

  7. 【刷题笔记】DP优化-状压

    因为篇幅太长翻着麻烦,计划把DP拆成几个小专题,这里原文只留下状压,其他请至后续博文. 状态压缩优化 所谓状态压缩,就是将原本需要很多很多维来描述,甚至暴力根本描述不清的状态压缩成一维来描述. 时间复 ...

  8. BZOJ 1076: [SCOI2008]奖励关 [DP 期望 状压]

    传送门 题意:$n$种宝物,出现$k$次每次一种,每种宝物有价值和吃掉它之前必须要吃掉的宝物的集合,求采取最优策略的期望最大价值 1<=k<=100,1<=n<=15,分值为[ ...

  9. CF 375C Circling Round Treasures [DP(spfa) 状压 射线法]

    C - Circling Round Treasures 题意: 在一个$n*m$的地图上,有一些障碍,还有a个宝箱和b个炸弹.你从(sx,sy)出发,走四连通的格子.你需要走一条闭合的路径,可以自交 ...

随机推荐

  1. H5 video标签的属性

    35-video标签 video标签的属性 src: 用于告诉video标签需要播放的视频地址 autoplay: 用于告诉video标签是否需要自动播放视频 controls: 用于告诉video标 ...

  2. matplotlib 入门之Pyplot tutorial

    文章目录 pyplot 介绍 修饰你的图案 格式字符串 [color][marker][line] Colors Markers Line Styles 利用关键字作图(大概是数据映射到属性吧) 传入 ...

  3. 使用 Markdown编辑

    作用: 学习笔记,整理日志, 发布日记,杂文,所见所想 撰写发布技术文稿(代码支持) 撰写发布学术论文(LaTeX 公式支持) sublime text3插件 输入 Shift + Ctrl + P, ...

  4. 关于function和task的说明

    1.  关于函数function调用,总结两个要点: 1.  函数调用一般产生一个值,这个值被赋值给某个变量 2.  函数所返回的值只能是一个,不可以是多个,不能像C语言中采用指针的方式返回多个值.因 ...

  5. Atcoder E - Knapsack 2 (01背包进阶版 ex )

    E - Knapsack 2 Time Limit: 2 sec / Memory Limit: 1024 MB Score : 100100 points Problem Statement The ...

  6. 逻辑回归为什么用sigmoid函数

    Logistic回归目的是从特征学习出一个0/1分类模型,而这个模型是将特性的线性组合作为自变量,由于自变量的取值范围是负无穷到正无穷. 因此,使用logistic函数(或称作sigmoid函数)将自 ...

  7. ajax相关问题

    1.contentType和dataType contentType 主要设置你发送给服务器的数据格式 dataType设置你收到服务器数据的格式(如text,json等),最常用的为json. 2. ...

  8. Innodb 实现高并发、redo/undo MVCC原理

    一.并发控制   因为并发情况下有可能出现不同线程对同一资源进行变动,所以必须要对并发进行控制以保证数据的同一与安全.   可以参考CPython解释器中的GIL全局解释器锁,所以说python中没有 ...

  9. python_超级基础

    初识计算机 CPU 计算机的大脑.中央处理单元,主要负责数据运算及计算,是运算计算中心. 存储器 内存 临时存储数据,供CPU运算使用. 优点: 读取速度快. 缺点: 容量小,成本高,断电即消失. 硬 ...

  10. rest-framework的认证组件

    认证组件 1.登录认证(与组件无关): 首先要在model表内添加用户表和token表: from django.db import models # Create your models here. ...