P1352 没有上司的舞会

作为一道经典例题,几乎学树形 \(DP\) 就得先做它。

设 \(f[i][j]\) ,当 \(j\) 为 \(0\) 时表示第 \(i\) 个人不来,当 \(j\) 为 \(1\) 时表示第 \(i\) 个人来,所以状态转移方程为:

\[f[x][1]+=f[y][0](y \in son(x))
\]
\[f[x][0]+=max(f[y][0],f[y][1])(y \in son(x))
\]
#include<bits/stdc++.h>
using namespace std;
int dp[200020][2];
int n;
int h[200003];
struct node{
int to,nxt;
}z[200003];
int cnt;
void add(int a,int b){
z[++cnt].to=b;
z[cnt].nxt=h[a];
h[a]=cnt;
}
int a[200003];
void dfs(int now,int fa){
dp[now][0]=0;
dp[now][1]=a[now];
for(int i=h[now];i;i=z[i].nxt){
int y=z[i].to;
if(y==fa) continue;
else{
dfs(y,now);
dp[now][0]+=max(dp[y][0],dp[y][1]);
dp[now][1]+=dp[y][0];
}
}
}
int in[200003];
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
for(int i=1;i<n;i++) {
int k,l;
scanf("%d%d",&l,&k);
add(k,l);
in[l]++;
}
int boss;
for(int i=1;i<=n;i++) if(!in[i]){ boss=i;break; }
dfs(boss,0);
cout<<max(dp[boss][1],dp[boss][0]);
}

P2014 [CTSC1997] 选课

其实就是一个 \(01\) 背包。

设 \(f[i][j]\) 表示以 \(i\) 为根的子树中选了 \(j\) 个点的最大价值。

对于点 \(x\) 遍历自己的儿子们,然后不断与自己合并。

#include<bits/stdc++.h>
using namespace std;
struct node{
int nxt,to;
}z[305];
int n,m,cnt;
int h[305];
int dp[305][304];
void add(int u,int v){
cnt++;
z[cnt].nxt=h[u];
z[cnt].to=v;
h[u]=cnt;
}
void dfs(int x){
for(int i=h[x];i;i=z[i].nxt){
int y=z[i].to;
dfs(y);
for(int j=m;j>=1;j--){
for(int k=0;k<j;k++){
dp[x][j]=max(dp[x][j],dp[x][j-k]+dp[y][k]);
}
}
}
}
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++){
int a,b;
scanf("%d%d",&a,&dp[i][1]);
add(a,i);
}
m++;
dfs(0);
printf("%d",dp[0][m]);
}

P3478 [POI2008] STA-Station

我们可以先从第 \(1\) 个节点搜,预处理出当以 \(1\) 为根的深度和还有每个点的子树大小。

然后再来一遍 \(DFS\) 我们发现,当以 \(x\) 为根时,对于以 \(x\) 的父亲为根时,自己的子树的深度都会减 \(1\),而其他的点的深度都会加 \(1\),所以就得到了状态转移方程。

#include<bits/stdc++.h>
#define int long long
using namespace std;
int n;
struct node{
int to,nxt;
}z[2000004];
int h[1000004];
int cnt;
void add(int x,int y){
z[++cnt].to=y;
z[cnt].nxt=h[x];
h[x]=cnt;
}
int dep[1000004];
int siz[1000004];
int sum_dep[1000005];
void dfs(int x,int fa){
siz[x]=1;
dep[x]=dep[fa]+1;
for(int i=h[x];i;i=z[i].nxt ){
int y=z[i].to;
if(y==fa) continue;
else{
dfs(y,x);
siz[x]+=siz[y];
}
}
}
int f[1000004];
void DFS(int x,int fa){
for(int i=h[x];i;i=z[i].nxt){
int y=z[i].to;
if(y==fa) continue;
else{
f[y]=f[x]-1ll*2*siz[y]+n;
DFS(y,x);
}
}
}
signed main(){
scanf("%lld",&n);
for(int i=1;i<n;i++){
int u,v;
scanf("%lld%lld",&u,&v);
add(u,v);
add(v,u);
}
dfs(1,0);
int t;
int maxx=0;
for(int i=1;i<=n;i++) f[1]+=dep[i];
DFS(1,0);
for(int i=1;i<=n;i++){
//maxx=max(maxx,f[i]);
if(maxx<f[i]){
maxx=f[i];
t=i;
}
}
cout<<t<<endl;
}

P2607 [ZJOI2008] 骑士

这是一颗基环树,所以我们肯定不能上来就做。

我们可以先找到环,然后跑两次 \(DFS\) 就好了

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e6+10;
int f[N][2];
int val[N];
int n;
struct node{
int to,nxt;
}z[2*N];
int cnt;
int h[N];
int F[N];
int vis[N];
void add(int x,int y){
z[++cnt].to=y;
z[cnt].nxt=h[x];
h[x]=cnt;
}
int root;
void dp(int x){
f[x][0]=0;
f[x][1]=val[x];
vis[x]=1;
for(int i=h[x];i;i=z[i].nxt){
int y=z[i].to;
if(y!=root){
dp(y);
f[x][0]+=max(f[y][0],f[y][1]);
f[x][1]+=f[y][0];
}
else{
f[y][1]=-1e9;
}
}
}
int ans; void Find(int x){
root=x;
vis[x]=1;
while(!vis[F[root]]){
root=F[root];
vis[root]=1;
}
dp(root);
int t=max(f[root][1],f[root][0]);
vis[root]=1;
root=F[root];
dp(root);
ans+=max(t,max(f[root][1],f[root][0]));
}
signed main(){
cin>>n;
for(int i=1;i<=n;i++){
int fa;
cin>>val[i]>>fa;
F[i]=fa;
add(fa,i);
}
for(int i=1;i<=n;i++){
if(!vis[i]){
Find(i);
}
}
cout<<ans;
}

P1896 [SCOI2005] 互不侵犯

一道很普通的状压 DP。

首先预处理出所有状态(即代码中的 \(dfs\) )。

然后定义 \(f[i][j][k]\) 表示d第 \(i\) 行的状态为 \(j\),一共选了 \(k\) 个。

#include<bits/stdc++.h>
#define int long long
using namespace std;
int n,K;
int sum_s[20090];
int f[10][120][100];
int cnt;
int s[20004];
void dfs(int x,int sum,int y){
if(y>=n){
s[++cnt]=x;
sum_s[cnt]=sum;
return ;
}
dfs(x,sum,y+1);
dfs(x+(1<<y),sum+1,y+2); //因为两个King不能相邻
}
signed main(){
cin>>n>>K;
dfs(0,0,0);
for(int i=1;i<=cnt;i++) f[1][i][sum_s[i]]=1;
for(int i=2;i<=n;i++){
for(int j=1;j<=cnt;j++){
for(int k=1;k<=cnt;k++){
if(s[j]&s[k]) continue;
if((s[j]<<1)&s[k]) continue;
if(s[j]&(s[k]<<1)) continue;
for(int p=K;p>=sum_s[j];p--){
f[i][j][p]+=f[i-1][k][p-sum_s[j]];
}
}
}
}
int ans=0;
for(int i=1;i<=cnt;i++) ans+=f[n][i][K];
cout<<ans;
}

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

  1. 树形DP和状压DP和背包DP

    树形DP和状压DP和背包DP 树形\(DP\)和状压\(DP\)虽然在\(NOIp\)中考的不多,但是仍然是一个比较常用的算法,因此学好这两个\(DP\)也是很重要的.而背包\(DP\)虽然以前考的次 ...

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

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

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

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

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

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

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

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

  6. hoj 2662 经典状压dp // MyFirst 状压dp

    题目链接:http://acm.hit.edu.cn/hoj/problem/view?id=2662 1.引言:用dp解决一个问题的时候很重要的一环就是状态的表示,一般来说,一个数组即可保存状态. ...

  7. 【BZOJ3925】地震后的幻想乡(期望概率DP,状压DP)

    题意:给定一张点数不超过10的无向连通图,每条边有一个[0,1]之间的随机权值,求最小生成树上最大边的期望值 提示:对于n个[0,1]之间的随机变量x1,x2,...,xn,第k小的那个的期望值是k/ ...

  8. hdu4352 XHXJ's LIS[数位DP套状压DP+LIS$O(nlogn)$]

    统计$[L,R]$内LIS长度为$k$的数的个数,$Q \le 10000,L,R < 2^{63}-1,k \le 10$. 首先肯定是数位DP.然后考虑怎么做这个dp.如果把$k$记录到状态 ...

  9. 【dp】状压dp

    二进制的力量 状态压缩DP 愤怒的小鸟 第一次接触状态压缩DP是在NOIP2016的愤怒的小鸟,当时菜得连题目都没看懂,不过现在回过头来看还是挺简单的,那么我们再来看看这道题吧. 题意&数据范 ...

  10. poj3254 Corn Fields (状压DP)

    http://poj.org/problem?id=3254 Corn Fields Time Limit: 2000MS   Memory Limit: 65536K Total Submissio ...

随机推荐

  1. openpyxl 写入字典

    def write(self,data_path, sheetname,value): index = len(value) workbook = openpyxl.Workbook() sheet ...

  2. Linux下yum安装mysql 遇到的问题Can't open and lock privilege tables: Table 'mysql.user' doesn't exist 错误

    今天在linux下安装mysql时 执行service mysqld start时, mysql总是启动失败 后来查看mysql日志:/var/log/mysqld.log,发现有个Can't ope ...

  3. 鸿蒙WebSocket的使用竟如此简单

    使用WebSocket建立服务器与客户端的双向连接,需要先通过createWebSocket()方法创建WebSocket对象,然后通过connect()方法连接到服务器.当连接成功后,客户端会收到o ...

  4. rust学习笔记(5)

    函数 定义为 fn is_divisible_by(lhs: u32, rhs: u32) -> bool { 使用 -> 指向返回的类型 函数定义的位置没有限制 method 针对结构体 ...

  5. vue2 配置 mock.js 模拟后端数据

    安装 mockj 首先确保你有一个 vue 2 项目,如果没有,可以用 Vue CLI 创建一个: vue create vue-mock-demo 开始安装 Mock.js npm install ...

  6. Nginx 之fastcgi常用配置项说明

    在LNMP环境中,我们都知道nginx如果要解析php脚本语言,就必须通过配置fastcgi模块来提供对php支持,那么在配置fastcgi的时候,关于fastcgi配置项的值应该怎么设置才能让其发挥 ...

  7. Oracle12c 数据库 警告日志

    目录 一:查看警告日志文件的位置 二:警告日志内容 三:告警日志监控: 方案1: 方案2: 方案3: 正文 回到顶部 一:查看警告日志文件的位置 Oracle 12c环境下查询,alert日志并不在b ...

  8. 关于Primavera P6版本选择上的一些看法

    从开始接触P6到目前也有近6年的时间,从最开始用的V7 (除P6.2.1)到现在用的18.8.0 ,除去一些小版本,中间自己跨越了8个不同版本    7.0,  (2013)    8.2       ...

  9. 二分查找--java进阶day06

    1.二分查找 https://kdocs.cn/l/ciMkwngvaWfz?linkname=150996908 二分查找:每一次查找都从中间的元素查起,根据比较的大小来折半,以此类推,直到最后找到 ...

  10. 【Markdown】简明语法手册

    Cmd Markdown 简明语法手册 标签: Cmd-Markdown 1. 斜体和粗体 使用 * 和 ** 表示斜体和粗体. 示例: 这是 *斜体*,这是 **粗体** 这是 斜体,这是 粗体. ...