树形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. 【转载】KETTLE集群搭建

    一.集群的原理与优缺点 1.1集群的原理 Kettle集群是由一个主carte服务器和多个从carte服务器组成的,类似于master-slave结构,不同的是’master’处理具体任务,只负责任务 ...

  2. 对Vuejs框架原理名词解读

    渐进式()+虚拟Dom: vue-cli 遍历Dom:先序遍历DOM树的5种方法! 三层架构+m v c +mvp+m v vm()+MVC,MVP 和 MVVM 的图示 剖析vue MVVM实现原理 ...

  3. uva11300 分金币(中位数)

    来源:https://vjudge.net/problem/UVA-11300 题意: 有n个人围成一圈,每个人有一定数量的金币,每次只能挪动一个位置,求挪动的最少金币使他们平分金币 题解: 蓝书p6 ...

  4. Codeforces Round #481 (Div. 3)Petya's Exams CodeForces - 978G

    Petya studies at university. The current academic year finishes with nn special days. Petya needs to ...

  5. 03-命令图片.doc

  6. C#中使用打印日志

    在日常的工作中经常需要日志,这样能够很容易定位到代码中的一些错误,.Net中有自带的日志接口.并没有仔细去研究,这里是我自己写的日志接口,记录下来以便以后用到,根据时间打印相关的日志文件,代码如下: ...

  7. Go To Oracle

    1.下载mingw   (gcc 编译)---win32 2.下载OCI最新版,存放于C:\instantclient_12_1   ---win32 3.下载OCI SDK最新版,存放于C:\ins ...

  8. spring mvc常用注解总结

    1.@RequestMapping@RequestMappingRequestMapping是一个用来处理请求地址映射的注解(将请求映射到对应的控制器方法中),可用于类或方法上.用于类上,表示类中的所 ...

  9. idea 创建的spingmvc 引入jquery后jquery函数始终不执行的原因

  10. 运维常用mysql语句

    1..select @@version; ##查询当前mysql的版本. 2. show variables like 'port';##查看mysql实例的端口. 3.show variables ...