动态规划——树形DP

devtools/2025/1/16 5:57:13/

题目清单

在这里插入图片描述

acwing285.没有上司的舞会

状态表示 d p [ u ] [ 0 / 1 ] dp[u][0/1] dp[u][0/1]
集合:对于以u节点为根的子树,选择(1)或不选择(0)u节点的方案。
属性: m a x max max
状态计算
d p [ u ] [ 0 ] = s u m ( m a x ( d p [ k ] [ 0 ] , d p [ k ] [ 1 ] ) ) dp[u][0]=sum(max(dp[k][0],dp[k][1])) dp[u][0]=sum(max(dp[k][0],dp[k][1]))k为u所有子节点
d p [ u ] [ 1 ] = s u m ( d p [ k ] [ 0 ] ) dp[u][1]=sum(dp[k][0]) dp[u][1]=sum(dp[k][0])

#include <iostream>
#include <cstring>
using namespace std;
const int N = 6010;
int happy[N];
int h[N], e[N], ne[N];
bool has_fa[N];
int dp[N][2];
int n, tot = 1;
void add(int a, int b)
{e[tot] = b, ne[tot] = h[a], h[a] = tot ++ ;
}
void dfs(int u)
{dp[u][1] = happy[u];for (int i = h[u]; ~i; i = ne[i]){int j = e[i];dfs(j);dp[u][0] += max(dp[j][0], dp[j][1]);dp[u][1] += dp[j][0];}return ;
}
int main()
{cin >> n;for (int i = 1; i <= n; i ++ )cin >> happy[i];memset(h, -1, sizeof h);for (int i = 1; i < n; i ++ ){int a, b;cin>> a >> b;has_fa[a] = 1;add (b, a);}int root;for (int i = 1; i <= n; i ++ )if (has_fa[i] == 0) root = i;dfs(root);cout << max(dp[root][0], dp[root][1]);return 0;
}

acwing1074.二叉苹果树

状态表示 d p [ u ] [ i ] [ j ] dp[u][i][j] dp[u][i][j]
集合:对于以u节点为根的子树,考虑到u的第i个子树并且总树枝数量为j的所有集合。
属性: m a x max max
状态计算
d p [ u ] [ i ] [ j ] = m a x ( d p [ u ] [ i − 1 ] [ j − k − 1 ] + d p [ s o n i ] [ c n t ( s o n i ) ] [ k ] + w u i ) dp[u][i][j]=max(dp[u][i-1][j-k-1]+dp[son_i][cnt(son_i)][k]+w_{ui}) dp[u][i][j]=max(dp[u][i1][jk1]+dp[soni][cnt(soni)][k]+wui)枚举k,即分给子树i的树枝数量。

在实际书写时可以把空间压缩至二维,省掉第二个维度。

#include <iostream>
#include <cstring>
using namespace std;
const int N = 110;
int h[N], e[N * 2], ne[N * 2], w[N * 2];
int dp[N][N];
int n, m, tot = 1;
void dfs(int u, int fa)
{for (int i = h[u]; ~i; i = ne[i]){if (e[i] == fa) continue;dfs(e[i], u);for (int j = m; j >= 0; j -- ){for (int k = 0; k < j; k ++ ){dp[u][j] = max(dp[u][j], dp[u][j - k - 1] + dp[e[i]][k] + w[i]);}}}return ;
}
void add(int a, int b, int c)
{e[tot] = b, w[tot] = c, ne[tot] = h[a], h[a] = tot ++ ;
}
int main()
{cin >> n >> m;memset(h, -1, sizeof h);for (int i = 1; i < n; i ++ ){int a, b, c;cin >> a >> b >> c;add(a, b, c), add(b, a, c);}dfs(1, -1);cout << dp[1][m];return 0;
}

acwing323.战略游戏

和没有上司的舞会不同的是,本题要求一条边上至少有一个点,和前者要求一条边上最多一个点。
状态表示 d p [ u ] [ 0 / 1 ] dp[u][0/1] dp[u][0/1]
集合:对于以u节点为根的子树,选择(1)或不选择(0)u节点的方案。
属性: m i n min min
状态计算
d p [ u ] [ 0 ] = s u m ( d p [ k ] [ 1 ] ) ) dp[u][0]=sum(dp[k][1])) dp[u][0]=sum(dp[k][1]))k为u所有子节点
d p [ u ] [ 1 ] = s u m ( m i n ( d p [ k ] [ 0 ] , d p [ k ] [ 0 ] ) ) dp[u][1]=sum(min(dp[k][0],dp[k][0])) dp[u][1]=sum(min(dp[k][0],dp[k][0]))

#include <iostream>
#include <cstring>
using namespace std;
const int N = 1510;
int n, tot;
bool st[N];
int dp[N][2];
int h[N], e[N], ne[N];
void add(int a, int b)
{e[tot] = b, ne[tot] = h[a], h[a] = tot ++ ;
}
void dfs(int u)
{dp[u][1] = 1;dp[u][0] = 0;for (int i = h[u]; ~i; i = ne[i]){int j = e[i];dfs(j);dp[u][1] += min(dp[j][0], dp[j][1]);dp[u][0] += dp[j][1];}return ;
}
int main()
{while(cin >> n){tot = 0;memset(h, -1, sizeof h);memset(st, 0, sizeof st);for (int i = 1; i <= n; i ++ ){int a, cnt, b;scanf("%d:(%d)", &a, &cnt);for (int i = 1; i <= cnt; i ++ ){cin >> b;st[b] = 1;add(a, b);}}int root = 0;while(st[root]) root ++ ; dfs(root);cout << min(dp[root][0], dp[root][1]) << endl;}return 0;
}

acwing1077.皇宫看守

和上一题不同的是,上一题中每个节点可以负责和它相邻的所有边,而本题中每个节点可以负责和他相邻的所有节点。
状态表示 d p [ u ] [ 0 / 1 / 2 ] dp[u][0/1/2] dp[u][0/1/2]
集合:对于以u节点为根的子树,u由子节点负责(1)或u由父节点负责(0)或u由自己负责(2)的方案。
属性: m i n min min
状态计算
d p [ u ] [ 0 ] = s u m ( m i n ( d p [ k ] [ 1 ] , d p [ k ] [ 2 ] ) ) dp[u][0]=sum(min(dp[k][1],dp[k][2])) dp[u][0]=sum(min(dp[k][1],dp[k][2]))
d p [ u ] [ 1 ] = m i n ( d p [ k ] [ 1 ] + s u m ( m i n ( d p [ s ] [ 1 ] , d [ s ] [ 2 ] ) ) ) dp[u][1]=min(dp[k][1]+sum(min(dp[s][1],d[s][2]))) dp[u][1]=min(dp[k][1]+sum(min(dp[s][1],d[s][2])))k为u的一个子结点,s为除去s的所有子节点
d p [ u ] [ 2 ] = m i n ( d p [ k ] [ 0 ] , d p [ k ] [ 1 ] , d p [ k ] [ 2 ] ) dp[u][2]=min(dp[k][0],dp[k][1],dp[k][2]) dp[u][2]=min(dp[k][0],dp[k][1],dp[k][2])

#include <iostream>
#include <cstring>
using namespace std;
const int N = 1510;
const int INF = 1e9;
int h[N], e[N], ne[N];
int n, tot;
int dp[N][3], w[N];
bool st[N];
void dfs(int u)
{dp[u][2] = w[u];for (int i = h[u]; ~i; i = ne[i]){int j = e[i];dfs(j);dp[u][2] += min(min(dp[j][0], dp[j][1]), dp[j][2]);dp[u][0] += min(dp[j][1], dp[j][2]);}dp[u][1] = INF;for (int i = h[u]; ~i; i = ne[i]){int j = e[i];dp[u][1] = min(dp[u][1], dp[j][2] + dp[u][0] - min(dp[j][1], dp[j][2]));}return ;
}
void add(int a, int b)
{e[tot] = b, ne[tot] = h[a], h[a] = tot ++ ;
}
int main()
{cin >> n;memset(h, -1, sizeof h);for (int i = 1; i <= n; i ++ ){int a, b, cnt;cin >> a >> w[a] >> cnt;while (cnt -- ){cin >> b;st[b] = 1;add(a, b);}}int root = 1;while(st[root]) root ++ ;dfs(root);cout << min(dp[root][1], dp[root][2]);return 0;
}

acwing1072.树的最长路径

求出每个点的最长路径d1和次长路径d2后,树上的最长路径就是 m a x ( d 1 + d 2 ) max(d1+d2) max(d1+d2)
状态表示 d p 1 [ u ] dp1[u] dp1[u]
集合:以u节点为根的子树到其他节点的路径。
属性: m a x max max
状态计算
d p 1 [ u ] = m a x ( d p 1 [ k ] + w u k ) dp1[u]=max(dp1[k]+w_{uk}) dp1[u]=max(dp1[k]+wuk)
d p 2 [ u ] = m a x ( d p 1 [ c ] + w u c ) dp2[u]=max(dp1[c]+w_{uc}) dp2[u]=max(dp1[c]+wuc) ( c ≠ k ) \ \ \ \ \ (c≠k)      (c=k)

#include <iostream>
#include <cstring>
using namespace std;
const int N = 10010;
int tot = 1, res = 0;
int e[N * 2], ne[N * 2], w[N * 2], h[N];
int n;
void add(int a, int b, int c)
{e[tot] = b, w[tot] = c, ne[tot] = h[a], h[a] = tot ++ ;
}
int dfs(int u, int fa)
{int d1 = 0, d2 = 0, dist = 0;for (int i = h[u]; ~i; i = ne[i]){if (e[i] == fa) continue;int d = dfs(e[i], u) + w[i];dist = max(dist, d);if (d1 <= d)  d2 = d1, d1 = d;else if (d > d2) d2 = d;}res = max(res, d1 + d2);return dist;
}
int main()
{cin >> n;memset(h, -1, sizeof h);for (int i = 1; i < n; i ++ ){int a, b, c;cin >> a >> b >> c;add(a, b, c), add(b, a, c);}dfs(1, -1);cout << res;return 0;
}

acwing1073.树的中心

求出每个点u的最长路径d1和次长路径d2,一个节点到其他节点的最远距离为 m a x ( d 1 , d f a 1 ) max(d1,d_{fa}1) max(d1,dfa1)——当路径 d f a 1 d_{fa}1 dfa1不经过u时;反之,最远距离就是 m a x ( d 1 , d f a 2 ) max(d1,d_{fa}2) max(d1,dfa2)
d f a 1 d_{fa}1 dfa1为父节点到其他节点的最远距离, d f a 2 d_{fa}2 dfa2为父节点到其他节点的次远距离。

#include <iostream>
#include <cstring>
using namespace std;
const int N = 10010;
const int INF = 0x3f3f3f3f;
int h[N], e[N * 2], ne[N * 2], w[N * 2];
int d1[N], d2[N], up[N], p1[N], p2[N];
int tot = 1;
int n;
void add (int a, int b, int c)
{e[tot] = b, w[tot] = c, ne[tot] = h[a], h[a] = tot ++ ;
}
void dfs1(int u, int fa)
{for (int i = h[u]; ~i; i = ne[i]){int j = e[i];if (j == fa) continue;dfs1(j, u);if (d1[u] <= d1[j] + w[i]){d2[u] = d1[u], d1[u] = d1[j] + w[i];p2[u] = p1[u], p1[u] = j;}else if (d2[u] < d1[j] + w[i]){d2[u] = d1[j] + w[i];p2[u] = j;}}return ;
}
void dfs2(int u, int fa)
{for (int i = h[u]; ~i; i = ne[i]){int j = e[i];if (j == fa) continue;if (p1[u] == j) up[j] = w[i] + max(up[u], d2[u]);   //son_u  = j,则用次大更新else up[j] = w[i] + max(up[u], d1[u]);              //son_u != j,则用最大更新dfs2(j, u);}return ;
}
int main()
{cin >> n;memset(h, -1, sizeof h);for (int i = 1; i < n; i ++ ){int a, b, c;cin >> a >> b >> c;add(a, b, c), add(b, a, c);}dfs1(1, -1);dfs2(1, -1);int res = INF;for (int i = 1; i <= n; i ++ ){res = min(res, max(d1[i], up[i]));}cout << res;return 0;
}

acwing1075.数字转换

本题本质上就是acwing.1072题加上一个背景。
先求出每个数对应的约数和,对于合法的数 i i i,就相当于把 i i i连接在约数和 s u m [ i ] sum[i] sum[i]上,把所有数都那么操作之后就得到了一棵树,这样一来,求不断进行数字变换且不出现重复数字的最多变换步数就相当于求数的最长直径。

#include <iostream>
#include <cstring>
using namespace std;
const int N = 50010;
int e[N], ne[N], w[N], h[N];
int sum[N];
int st[N];
int n;
int tot = 1;
int res = 0;
void add(int a, int b)
{e[tot] = b, ne[tot] = h[a], h[a] = tot ++ ;
}
int dfs(int u)
{int d1 = 0, d2 = 0;for (int i = h[u]; ~i; i = ne[i]){int j = e[i];int d = dfs(j) + 1;if (d >= d1) d2 = d1, d1 = d;else if (d > d2) d2 = d;}res = max(res, d1 + d2);return d1;
}
int main()
{cin >> n;memset(h, -1, sizeof h);for (int i = 1; i <= n; i ++ ){for (int j = 2; j * i <= n; j ++ )sum[i * j] += i;}for (int i = 1; i <= n; i ++ ){if (sum[i] < i){st[i] = 1;add(sum[i], i);}}for (int i = 1; i <= n; i ++ )if(st[i]) dfs(i);cout << res;return 0;
}

http://www.ppmy.cn/devtools/150863.html

相关文章

Node.js入门html,css,js 30年了nodejs环境 09年出现 15年

Node.js入门 html,css,js 30年了 nodejs环境 09年出现 15年 nodejs为我们解决了2个方面的问题&#xff1a; 【锦上添花】让我们前端工程师拥有了后端开发能力&#xff08;开接口&#xff0c;访问数据库&#xff09; - 大公司BFF&#xff08;50&#xff09;【✔️】前端工程…

深入理解计算机系统阅读笔记-第十二章

第12章 网络编程 12.1 客户端-服务器编程模型 每个网络应用都是基于客户端-服务器模型的。根据这个模型&#xff0c;一个应用时由一个服务器进程和一个或者多个客户端进程组成。服务器管理某种资源&#xff0c;并且通过操作这种资源来为它的客户端提供某种服务。例如&#xf…

虚假星标:GitHub上的“刷星”乱象与应对之道

在开源软件的世界里&#xff0c;GitHub无疑是最重要的平台之一。它不仅是一个代码托管平台&#xff0c;也是一个社交网络&#xff0c;允许开发者通过“点赞”&#xff08;即加星&#xff09;来表达对某个项目的喜爱和支持&#xff0c;“星标”&#xff08;Star&#xff09;则成…

机器学习基础-机器学习的常用学习方法

目录 半监督学习的概念 规则学习的概念 基本概念 机器学习里的规则 逻辑规则 规则集 充分性与必要性 冲突消解 命题逻辑 → 命题规则 序贯覆盖 单条规则学习 剪枝优化 强化学习的概念 1. 强化学习对应了四元组 2. 强化学习的目标 强化学习常用马尔可夫决策过程…

hadoop3.3和hive4.0安装——单节点

hadoop3.3x和hive4.0安装部署 为什么我要安装hive4.0&#xff0c;因为阿里云镜像只有hive4.0 软件相互兼容性版本 系统centos7 uname -a如果内核3.0以上可以用 安装jdk1.8以上的版本&#xff08;配置好环境变量&#xff09; hadoop3.3.x与hive4.0.x 创建目录 mkdir -p /us…

Java SpringBoot + Vue + Uniapp 集成JustAuth 最快实现多端三方登录!(QQ登录、微信登录、支付宝登录……)

注&#xff1a;本文基于 若依 集成just-auth实现第三方授权登录 修改完善&#xff0c;所有步骤仅代表本人如下环境亲测可用&#xff0c;其他环境需自辩或联系查看原因&#xff01; 系统环境 运行系统&#xff1a;Windows10专业版、Linux Centos7.6 Java 版本&#xff1a;1.8.0_…

【硬件介绍】Type-C接口详解

一、Type-C接口概述 Type-C接口特点&#xff1a;以其独特的扁头设计和无需区分正反两面的便捷性而广受欢迎。这种设计大大提高了用户的使用体验&#xff0c;避免了传统USB接口需要多次尝试才能正确插入的问题。Type-C接口内部结构&#xff1a;内部上下两排引脚的设计虽然可能不…

redhat安装docker 24.0.7

1、下载docker镜像包 wget https://download.docker.com/linux/static/stable/x86_64/docker-24.0.7.tgz 2、解压 tar -xvf docker-24.0.7.tgz 3、解压的docker文件夹全部移动至/usr/bin目录 cd docker cp -p docker/* /usr/bin 4、注册服务 vi /usr/lib/systemd/syste…