「学习笔记」DP学习笔记 2
树形DP#
树形 DP,即在树上进行的 DP。由于树固有的递归性质,树形 DP 一般都是 递归 进行的。
题目#
CF1528A
多组数据 ( 组)
给你大小为 的一棵树, 号节点有权值范围 ,让你对每个节点赋予一个权值 ,使得每个节点权值都在规定的范围里并且对于每条边 , 最大,并求出这个最大值。
。
对于每一个节点,它的值只可能会是 和 两种情况。
证明:
对于节点 ,我们假设与它相连的其他点的权值我们都已经确定了,这些点的权值会有 和 这两种情况。我们初设 ,随后逐渐 ,如果权值 的点的个数大于权值 的个数,则答案会增加,转化成函数就是一个单调递增函数;如果权值 的点的个数小于等于权值 的个数,则答案会减小或不变,但是随着 的增加,权值 的点的个数也在逐渐减少,所以函数图像是一个开口向上的单峰函数或单调递减函数。
单调函数和开口向上的单峰函数,极值都在左右两个端点上,所以每个点的取值只有 和 两种。
状态:: 号节点取最小值或最大值的最大价值。
转移:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
inline ll read() {
ll x = 0;
int fg = 0;
char ch = getchar();
while (ch < '0' || ch > '9') {
fg |= (ch == '-');
ch = getchar();
}
while (ch >= '0' && ch <= '9') {
x = (x << 3) + (x << 1) + (ch ^ 48);
ch = getchar();
}
return fg ? ~x + 1 : x;
}
const int N = 2e5 + 5;
int T, n;
ll val[N][2], dp[N][2];
vector<int> e[N];
void dfs(int u, int fat) {
for (int v : e[u]) {
if (v == fat) continue;
dfs(v, u);
for (int i = 0; i < 2; ++ i) {
ll maxn = 0;
for (int j = 0; j < 2; ++ j) {
maxn = max(maxn, dp[v][j] + abs(val[u][i] - val[v][j]));
}
dp[u][i] += maxn;
}
}
}
void work() {
n = read();
for (int i = 1; i <= n; ++ i) {
dp[i][1] = dp[i][0] = 0;
val[i][0] = read(), val[i][1] = read();
e[i].clear();
}
for (int i = 1, x, y; i < n; ++ i) {
x = read(), y = read();
e[x].emplace_back(y);
e[y].emplace_back(x);
}
dfs(1, 0);
printf("%lld\n", max(dp[1][0], dp[1][1]));
}
int main() {
T = read();
while (T --) {
work();
}
return 0;
}
P3174 [HAOI2009] 毛毛虫
对于一棵树,我们可以将某条链和与该链相连的边抽出来,看上去就象成一个毛毛虫,点数越多,毛毛虫就越大。求最大的毛毛虫的大小。
状态::以 为毛毛虫的头,最大的毛毛虫的大小,:以 为毛毛虫的头,次大的毛毛虫的大小
转移:
对于答案的判定,我们要把 点的 和 加起来,加上 (加上自己),同时还要处理与它相连的边,即他的所有孩子的数量(包括父节点)。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
inline ll read() {
ll x = 0;
int fg = 0;
char ch = getchar();
while (ch < '0' || ch > '9') {
fg |= (ch == '-');
ch = getchar();
}
while (ch >= '0' && ch <= '9') {
x = (x << 3) + (x << 1) + (ch ^ 48);
ch = getchar();
}
return fg ? ~x + 1 : x;
}
const int N = 3e5 + 5;
int n, m, ans;
int f[N], g[N];
vector<int> e[N];
void dfs(int u, int fat) {
for (int v : e[u]) {
if (v == fat) continue;
dfs(v, u);
if (f[v] >= f[u]) {
g[u] = f[u];
f[u] = f[v];
}
else if (f[v] > g[u]) {
g[u] = f[v];
}
}
int cnt = e[u].size() - (fat != 0);
ans = max(ans, f[u] + g[u] + 1 + max(0, cnt - 1 - (fat == 0)));
f[u] += (1 + max(0, cnt - 1));
}
int main() {
n = read(), m = read();
for (int i = 1, a, b; i <= m; ++ i) {
a = read(), b = read();
e[a].emplace_back(b);
e[b].emplace_back(a);
}
dfs(1, 0);
printf("%d\n", ans);
return 0;
}
P2899 [USACO08JAN]Cell Phone Network G
个点的树,你要放置数量最少的信号塔,保证所有点要么有信号塔要么与信号塔相邻。求最少数量。
状态: : 号节点被自己覆盖/被孩子覆盖/被父亲覆盖
转移:
/*
The code was written by yifan, and yifan is neutral!!!
*/
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
template<typename T>
inline T read() {
T x = 0;
bool fg = 0;
char ch = getchar();
while (ch < '0' || ch > '9') {
fg |= (ch == '-');
ch = getchar();
}
while (ch >= '0' && ch <= '9') {
x = (x << 3) + (x << 1) + (ch ^ 48);
ch = getchar();
}
return fg ? ~x + 1 : x;
}
const int N = 1e4 + 5;
int n;
int dp[N][3];
vector<int> e[N];
void dfs(int u, int fat) {
dp[u][0] = 1;
int p = 0, id = 0, fg = 0;
for (int v : e[u]) {
if (v == fat) continue;
dfs(v, u);
fg = 1;
dp[u][0] += min(dp[v][0], min(dp[v][1], dp[v][2]));
dp[u][2] += min(dp[v][1], dp[v][0]);
dp[u][1] += min(dp[v][1], dp[v][0]);
if (dp[v][0] <= dp[v][1]) {
p = v;
}
if (dp[v][0] < dp[id][0]) {
id = v;
}
}
if (fg && p == 0) {
dp[u][1] += (dp[id][0] - dp[id][1]);
}
if (!fg) {
dp[u][1] = 1e9;
}
}
int main() {
dp[0][0] = 1e9;
n = read<int>();
for (int i = 1, x, y; i < n; ++ i) {
x = read<int>(), y = read<int>();
e[x].emplace_back(y);
e[y].emplace_back(x);
}
dfs(1, 0);
printf("%d\n", min(dp[1][1], dp[1][0]));
return 0;
}
P2986
个农场形成一棵树,边有长度。第 个农场里有 只奶牛。
集会会在一个农场举行,所有的奶牛会沿最近的道路到达集会农场。求所有奶牛路程之和的最小值。
换根 DP
状态: 表示子树的奶牛到 的代价, 表示所有奶牛到 的代价, 子树奶牛数。
转移:第一遍算出 ,很好算。第二遍根据 自上而下算出 。
/*
The code was written by yifan, and yifan is neutral!!!
*/
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int, ll> pil;
template<typename T>
inline T read() {
T x = 0;
bool fg = 0;
char ch = getchar();
while (ch < '0' || ch > '9') {
fg |= (ch == '-');
ch = getchar();
}
while (ch >= '0' && ch <= '9') {
x = (x << 3) + (x << 1) + (ch ^ 48);
ch = getchar();
}
return fg ? ~x + 1 : x;
}
const int N = 1e5 + 5;
int n;
ll sum, ans;
int c[N], siz[N];
ll dp[N], f[N];
vector<pil> e[N];
void dfs(int u, int fat) {
siz[u] = c[u];
for (pil it : e[u]) {
int v = it.first;
ll w = it.second;
if (v == fat) continue;
dfs(v, u);
siz[u] += siz[v];
dp[u] = dp[u] + dp[v] + w * siz[v];
}
}
void Dp(int u, int fat) {
if (u == 1) {
f[u] = dp[u];
}
bool fg = 1;
for (pil it : e[u]) {
int v = it.first;
ll w = it.second;
if (v == fat) continue;
fg = 0;
f[v] = 1ll * f[u] - siz[v] * w + (sum - siz[v]) * w;
ans = min(ans, f[v]);
Dp(v, u);
}
if (fg) {
f[u] = 1e18;
}
}
int main() {
n = read<int>();
for (int i = 1; i <= n; ++ i) {
c[i] = read<int>();
sum += c[i];
}
for (int i = 1, x, y; i < n; ++ i) {
x = read<int>(), y = read<int>();
ll z = read<ll>();
e[x].emplace_back(y, z);
e[y].emplace_back(x, z);
}
dfs(1, 0);
ans = dp[1];
Dp(1, 0);
printf("%lld\n", ans);
return 0;
}
作者:yifan0305
出处:https://www.cnblogs.com/yifan0305/p/17481057.html
版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。
转载时还请标明出处哟!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!
2022-06-27 「学习笔记」LCA——树上倍增