首页 > 其他分享 >树形dp

树形dp

时间:2024-07-22 19:08:28浏览次数:15  
标签:子树 int 路径 dfs 树形 ans 节点 dp

含义


顾名思义,在树上dp,一般要分类讨论,在dfs中做dp

 

没有上司的舞会


https://www.luogu.com.cn/problem/P1352

 

Ural 大学有 N 名职员,编号为 1∼N。

他们的关系就像一棵以校长为根的树,父节点就是子节点的直接上司。

每个职员有一个快乐指数,用整数 Hi 给出,其中 1≤i≤N。

现在要召开一场周年庆宴会,不过,没有职员愿意和直接上司一起参会。

在满足这个条件的前提下,主办方希望邀请一部分职员参会,使得所有参会职员的快乐指数总和最大,求这个最大值。

输入格式

 

第一行一个整数 N。

接下来 N 行,第 i 行表示 i 号职员的快乐指数 H[i]。

接下来 N−1 行,每行输入一对整数 L,K,表示 L是 K 的直接上司。

1≤N≤6000,

−128≤H[i]≤127

 

输出格式

 

输出最大的快乐指数。

 

输入/输出例子1

输入:

7

1

1

1

1

1

1

1

1 3

2 3

6 4

7 4

4 5

3 5

 

输出:

5

 

样例解释

 

 

考虑分类讨论,某个节点去和不去,那么会对这个节点的子树造成一定影响

f[i][0/1]: 表示以i为根的子树 (节点i ) 去(1) 或 不去(0) 的点权最大值

由于每个以i为根的子树对i的贡献是相互独立的,所以分每个子树进行讨论,我们只需要知道一颗子树如何转移,另外的子树同理

设v是u的子树,转移方程显而易见

f[u][0]+=max(f[v][1], f[v][0])       //u节点不去,v节点可去可不去
f[u][1]+=f[v][0]      //u节点不去,v节点绝对不能去

初始化时,f[u][1]=h[u],即当u节点去时,最大值先假设为他自己的权值

 

#include <bits/stdc++.h>
using namespace std;
const int N=6005;

int n, h[N], u1, v1, root, f[N][3];
vector<int> a[N];
bool d[N];
void dfs(int u, int fa)
{
	f[u][1]=h[u];
	f[u][0]=0;
	
	for (int i=0; i<a[u].size(); i++)
	{
		int v=a[u][i];
		if (v==fa) continue;
		dfs(v, u);
		
		f[u][1]+=f[v][0];
		f[u][0]+=max(f[v][1], f[v][0]);
	}
}
int main()
{
	scanf("%d", &n);
	for (int i=1; i<=n; i++) scanf("%d", &h[i]);
	
	for (int i=1; i<n; i++)
	{
		scanf("%d%d", &u1, &v1);
		a[v1].push_back(u1);
		d[u1]=1;
	}
	
	for (int i=1; i<=n; i++) 
		if (!d[i])
		{
			root=i;
			break;
		}
	
	dfs(root, -1);
	
	printf("%d", max(f[root][0], f[root][1]));
	return 0;
}

  

 

 

树的最长直径


给定一棵树,树中包含 n 个结点(编号1~n)和 n−1 条无向边,每条边都有一个权值。

现在请你找到树中的一条最长路径。

换句话说,要找到一条路径,使得路径两端的点的距离最远。

注意:路径中可以只包含一个点。

输入格式

 

第一行包含整数 n。

接下来 n−1 行,每行包含三个整数 a[i],b[i],c[i],表示点 a[i] 和 b[i] 之间存在一条权值为 c[i] 的边。

1≤n≤10000,

1≤a[i],b[i]≤n,

−1e5≤ci≤1e5

 

输出格式

 

输出一个整数,表示树的最长路径的长度。

 

输入/输出例子1

输入:

6

5 1 6

1 4 5

6 3 9

2 6 8

6 1 7

 

输出:

22

 

样例解释

 

暴力


 (别人的代码,将就着看着吧)

#include <bits/stdc++.h>
using namespace std;

const int N = 10010, M = N << 1;
//  暴力搜索,从每个节点为根出发,遍历整根树,找出距离自己的最大距离,然后每个最大距离取min
//   11/17,其它TLE,无法AC
int n;
int ans; // 树的直径
// 邻接表
int e[M], h[N], idx, w[M], ne[M];
void add(int a, int b, int c = 0) {
    e[idx] = b, ne[idx] = h[a], w[idx] = c, h[a] = idx++;
}

void dfs(int u, int fa, int sum) {
    if (sum > ans) ans = sum;
    for (int i = h[u]; ~i; i = ne[i]) {
        int v = e[i];
        if (v == fa) continue; // 不走回头路
        dfs(v, u, sum + w[i]);
    }
}

int main() {
    // 初始化邻接表
    memset(h, -1, sizeof h);

    cin >> n;
    for (int i = 1; i < n; i++) { // n-1条边
        int a, b, c;
        cin >> a >> b >> c;
        add(a, b, c), add(b, a, c); // 无向图
    }

    // 多次dfs,是TLE的罪魁祸首
    for (int i = 1; i <= n; i++) dfs(i, 0, 0);

    // 输出结果
    printf("%d", ans);
    return 0;
}

  

两次解法dfs


优点:思路简单

缺点:只适用于不带负边的树

 传送门1号   传送门2号

 (别人的代码)

#include <bits/stdc++.h>

using namespace std;

const int N = 10010, M = N << 1;

int ans; // 保存最长路径
int t;   // 保存找到的最远点
int n;

// 邻接表
int e[M], h[N], idx, w[M], ne[M];
void add(int a, int b, int c = 0) {
    e[idx] = b, ne[idx] = h[a], w[idx] = c, h[a] = idx++;
}

void dfs(int u, int fa, int sum) {
    if (sum > ans) {
        ans = sum; // 记录最大距离
        t = u;     // 记录最远的点t1
    }
    for (int i = h[u]; ~i; i = ne[i]) {
        int v = e[i];
        if (v == fa) continue;
        dfs(v, u, sum + w[i]);
    }
}

int main() {
    memset(h, -1, sizeof h);
    cin >> n;
    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, 0, 0); // 先找到点距离点1最远的点t1

    dfs(t, 0, 0); // 找到距离点t1->t2最远的点t1

    printf("%d", ans);
    return 0;
}

  

 

最长+次长解法


树的最长路径 ,也称为树的 直径 ,直径 不唯一

我们知道:树上 任意两点 的路径是 唯一 确定的,因此我们可以暴力枚举 起点 和 终点 找出最长路径

如果这样做的话,我们来思考一下时间复杂度:

 

这里找出两点之间的路径有很多种做法,预处理 st 表然后倍增求LCA、树链剖分、Link-Cut Tree

 

 

 本图中f[x]表示x的子节点到x的最长距离,而非以x为顶点的最长路径.因为本题的路径是两条边,所以f[x]表示其中一条边的最大的值,f[y],f[z]同理,两条边加起来即为以u为顶点最长路径

 注:本题的答案不一定为以u为顶点的路径,因为u到x,y,z的距离可能均为负数,所以答案可以是以x,y或者z为顶点的路径
本题采用dfs的方法来求解,所以是求解方式自下而上的

 

 

补充

 

 

#include <bits/stdc++.h>
using namespace std;
const int N=10005;

struct node
{
	int v, w;
};
int n, u1, v1, w1, root=0, dis[N], dis2[N], ans=0;
bool d[N];
vector<node> a[N];
void dfs(int u, int fa)
{
	for (int i=0; i<a[u].size(); i++)
	{
		int v=a[u][i].v, w=a[u][i].w;
		if (v==fa) continue;
		dfs(v, u);
		
		int x=dis[v]+w;
		if (dis[u]<=x) dis2[u]=dis[u], dis[u]=x;
		else if (dis2[u]<x) dis2[u]=x;
	}
	ans=max(ans, dis[u]+dis2[u]);
}
int main()
{
	scanf("%d", &n);
	for (int i=1; i<n; i++)
	{
		scanf("%d%d%d", &u1, &v1, &w1);
		a[u1].push_back({v1, w1});
		a[v1].push_back({u1, w1});
		
		d[v1]=1;
	}
	
	for (int i=1; i<=n; i++)
		if (!d[i])
		{
			root=i;
			break;
		}
	
	dfs(root, -1);
	
	printf("%d", ans);
	return 0;
}

  

 参考:

https://www.cnblogs.com/littlehb/p/15784687.html

https://blog.csdn.net/qq_45896050/article/details/122777766

 

 

 

 

 

 

 

 

 

 

标签:子树,int,路径,dfs,树形,ans,节点,dp
From: https://www.cnblogs.com/didiao233/p/18316683

相关文章

  • dp乱刷
    CF1271DPortals思维点:每个城堡可以在其最晚可以被派遣到的时间被派遣,因为在最晚时间之前派遣的方案可以直接变为对应的在最晚时间派遣的方案。然后尽情DP即可。P1450[HAOI2008]硬币购物巧妙的容斥。首先按无限背包做,然后减去某一种硬币\(i\)超额的方案数,即\(f[s-(d_i+1)*c_......
  • 易优CMS模板标签downloadpay下载付费
    [基础用法]标签:downloadpay描述:下载模型实现付费,会员专享,会员付费下载,在使用之前先频道模型->下载模型->编辑->开启下载付费使用方法:付费标签需要加载/template/pc/system/download_pay.htm模板文件下载地址:点击下载,该文件放在pc模板目录......
  • 探索ESP32-A2DP:一个强大的蓝牙音频解决方案
    探索ESP32-A2DP:一个强大的蓝牙音频解决方案项目简介是一个基于EspressifSystemsESP32微控制器的开源项目,它实现了Bluetooth低能耗(BLE)和高级音频分布配置文件(A2DP)。这个项目允许你的ESP32设备作为高质量的蓝牙音频播放器,可以接收来自任何支持A2DP源的设备(如智能手机、电脑)的音频......
  • Web前端WebRTC攻略-媒体协商与SDP简析(转载)
    1.媒体协商在音视频通讯场景中,由于两端之间所支持的音视频编解码、传输协议、传输的速率,都需要进行彼此通知对方。我们把一个1对1的音视频通讯,比喻成双方互送快递包裹的过程。首先这里有很多问题,双方要彼此告知对方后,才能寄送包裹。比如:*我不知道包裹要寄给谁?(我要和谁建立通......
  • 区间dp入门
    [NOI1995]石子合并题目描述在一个圆形操场的四周摆放\(N\)堆石子,现要将石子有次序地合并成一堆,规定每次只能选相邻的\(2\)堆合并成新的一堆,并将新的一堆的石子数,记为该次合并的得分。试设计出一个算法,计算出将\(N\)堆石子合并成\(1\)堆的最小得分和最大得分。输入格......
  • 【普及动规】dp例题精讲+强化练习
    本篇给大家带来一些好的dp题,大家可以学习一下。找找感觉。dp这种东西主要还是靠分类总结+感觉。多练习永远不错。T1.害羞的xxx题面:(由于某些原因无法公开原题,请见谅)题目背景保护好xxx,因为他随时会害羞。题目描述众所周知,xxx非常害羞。可是学校最近在选拔芭蕾舞演员......
  • DDP
    线段树+树剖/\(lct\)维护广义矩阵乘法从例题开始讲P4719如果不带修改,那么就好做了\(f_{i,1/0}\)表示\(i\)节点选或不选的最大权容易得到转移\[ f_{i,0}=\sum_{son}max(f_{son,0},f_{son,1})\]\[ f_{i,1}=w_i+\sum_{son}f_{son,0}\]但是现在带修。你会......
  • Microsoft Endpoint Manager(MEM)是微软的一体化端点管理平台,结合了Microsoft Intune和C
    MicrosoftEndpointManager(MEM)是微软的一体化端点管理平台,结合了MicrosoftIntune和ConfigurationManager(SCCM),为企业提供跨设备、跨平台的终端管理和安全性管理能力。主要特点和功能包括:统一管理控制台:MEM提供了统一的管理控制台,使IT管理员可以从一个地方管理和监控企业中的......
  • 算法随笔——DP优化
    单调队列优化DP单调队列模板:inthead=1,tail=0; for(inti=1;i<=n;i++) { while(head<=tail&&head不满足条件)head++;//踢出队列 f[i]=f[q[head]]+...; while(head<=tail&&tail与i不满足单调性)tail--; q[++tail]=i; }优化思路则是......
  • A. Tokitsukaze and Strange Inequality(dp版)
    链接https://codeforces.com/problemset/problem/1677/A题目思路这题感觉还是挺有难度的(为啥题解都说不难Orz),给我启发最大的是这句话:具体怎么处理呢?把i按照n->1的顺序遍历,然后j从反方向遍历:i+1->n。求S[i][j]时用S[i+1][j],因为S对应的是以j为结尾的,然后在遍历中相当于不知......