树形DP和状压DP和背包DP
树形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的更多相关文章
- dp乱写1:状态压缩dp(状压dp)炮兵阵地
https://www.luogu.org/problem/show?pid=2704 题意: 炮兵在地图上的摆放位子只能在平地('P') 炮兵可以攻击上下左右各两格的格子: 而高原('H')上炮兵能 ...
- poj2411 Mondriaan's Dream (轮廓线dp、状压dp)
Mondriaan's Dream Time Limit: 3000MS Memory Limit: 65536K Total Submissions: 17203 Accepted: 991 ...
- BZOJ 4042 Luogu P4757 [CERC2014]Parades (树形DP、状压DP)
题目链接 (BZOJ) https://www.lydsy.com/JudgeOnline/problem.php?id=4042 (Luogu) https://www.luogu.org/prob ...
- dp,状压dp等 一些总结
也就作业几题而已,分析一下提醒 最重要的就是,记住,没用的状态无论怎么转移最后都会是没用的状态,所以每次转移以后的有值的状态都是有用的状态. 几种思考方向: 第一种:枚举当前的状态,转移成另外一个状态 ...
- 【BZOJ 4007】[JLOI2015]战争调度 DP+搜索+状压
又是一道思路清新的小清晰. 观察题目,如果我们确定了平民或者贵族的任意一方,我们便可以贪心的求出另一方,至此20分:我们发现层数十分小,那么我们就也是状压层数,用lca转移,线性dp,至此50分(好像 ...
- 【洛谷4045】[JSOI2009] 密码(状压+AC自动机上DP)
点此看题面 大致题意: 给你\(n\)个字符串,问你有多少个长度为\(L\)的字符串,使得这些字符串都是它的子串.若个数不大于\(42\),按字典序输出所有方案. 状压 显然,由于\(n\)很小,我们 ...
- 【刷题笔记】DP优化-状压
因为篇幅太长翻着麻烦,计划把DP拆成几个小专题,这里原文只留下状压,其他请至后续博文. 状态压缩优化 所谓状态压缩,就是将原本需要很多很多维来描述,甚至暴力根本描述不清的状态压缩成一维来描述. 时间复 ...
- BZOJ 1076: [SCOI2008]奖励关 [DP 期望 状压]
传送门 题意:$n$种宝物,出现$k$次每次一种,每种宝物有价值和吃掉它之前必须要吃掉的宝物的集合,求采取最优策略的期望最大价值 1<=k<=100,1<=n<=15,分值为[ ...
- CF 375C Circling Round Treasures [DP(spfa) 状压 射线法]
C - Circling Round Treasures 题意: 在一个$n*m$的地图上,有一些障碍,还有a个宝箱和b个炸弹.你从(sx,sy)出发,走四连通的格子.你需要走一条闭合的路径,可以自交 ...
随机推荐
- 【NLP】Attention Model(注意力模型)学习总结
最近一直在研究深度语义匹配算法,搭建了个模型,跑起来效果并不是很理想,在分析原因的过程中,发现注意力模型在解决这个问题上还是很有帮助的,所以花了两天研究了一下. 此文大部分参考深度学习中的注意力机制( ...
- Python 内置库 sys用法
sys模块功能众多,这边先学习几个常用的方法sys常见函数列表① sys.argv: 实现从程序外部向程序传递参数.其实sys.argv[]就是一个列表,里面的项为用户输入的参数,但是sys.argv ...
- Python里面如何拷贝一个对象
1.赋值(=),就是创建了对象的一个新的引用,修改其中任意一个变量都会影响到另一个. In [168]: a Out[168]: [1, 2, 3] In [169]: b=a In [170]: a ...
- DWZ富客户端框架(jQuery RIA framework)
该OA项目前端采用的是DWZ框架来进行实现的. 本来想写点总结的,但发现真没啥好写的.中文的文档,到时候用到直接看文档就好.
- TCP粘包问题解析与解决
一.粘包分析 作者本人在写一个FTP项目时,在文件的上传下载模块遇到了粘包问题.在网上找了一些解决办法,感觉对我情况都不好用,因此自己想了个比较好的解决办法,提供参考 1.1 粘包现象 在客户端与服务 ...
- java web 常见异常及解决办法
javax.servlet.ServletException: javax/servlet/jsp/SkipPageException 重启tomcat, javax.servlet.ServletE ...
- php使用gd库输出中文内容的图片
正如标题所说那样,本文只讨论输出内容全部为中文或者包含中文的情况.如果内容全是字母或者其他字符的话,可以参考这篇博客:生成验证码 问题 此处要注意,标题中为什么要区别windows和linux分别实现 ...
- WIN下修改host文件并立即生效
怎样修改WIN7下的host文件_百度经验https://jingyan.baidu.com/article/9faa72317903f1473c28cb01.html hosts立即生效的方法 - ...
- eclipse 中右键项目出现卡死导致无法共享项目的解决办法
亲身经历,这个问题出自于项目中的SVN地址不对,如果要更改SVN地址,可以断掉计算机的网,在eclipse的工作空间中找到该项目,找到隐藏的.svn 文件夹,删除掉之后,打开eclipse,此时就可以 ...
- opencv自带fast_math.hpp
cvRound cvFloor cvCeil cvIsNaN cvIsInf