(蓝桥杯 算法题)节点选择

本文介绍了一种使用树形动态规划(DP)解决在树状结构中选择节点的问题,目标是在遵循特定规则下使节点权值和最大化。通过构建邻接表,递归深度优先搜索(DFS),并采用二维DP数组来存储中间结果,实现了解决方案。文章提供了详细的代码示例和数据规模说明。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

问题描述

有一棵 n 个节点的树,树上每个节点都有一个正整数权值。如果一个点被选择了,那么在树上和它相邻的点都不能被选择。求选出的点的权值和最大是多少?

输入格式

第一行包含一个整数 n 。

接下来的一行包含 n 个正整数,第 i 个正整数代表点 i 的权值。

接下来一共 n-1 行,每行描述树上的一条边。

输出格式

输出一个整数,代表选出的点的权值和的最大值。

样例输入

5
1 2 3 4 5
1 2
1 3
2 4
2 5

样例输出

12

样例说明

选择3、4、5号点,权值和为 3+4+5 = 12 。

数据规模与约定

对于20%的数据, n <= 20。

对于50%的数据, n <= 1000。

对于100%的数据, n <= 100000。

权值均为不超过1000的正整数。

 

代码:(详细解析见注释)

1.	///邻接表构建过程  
2.	#include <iostream>  
3.	#include <cstdio>  
4.	#include <cstring>  
5.	#include <algorithm>  
6.	#include <vector>  
7.	using namespace std;  
8.	typedef long long LL;  
9.	  
10.	struct Edge{  
11.	    int edge;  
12.	    int next;  
13.	}e[300000];  
14.	  
15.	int head[300000],val[300000];  
16.	int N,M;  
17.	LL dp[150000][2]; ///dp[s/u][0/1],u为s的子节点,0表示包含自己的以s为根节点的子树最大值,1表示不包含自己的以s为根节点的子树最大值  
18.	bool v[150000];  
19.	  
20.	void add(int s,int ee)  
21.	{  
22.	    e[++M].edge = ee;           ///边的终点  
23.	    e[M].next = head[s];        ///邻边  
24.	    head[s] = M;                ///当前边  
25.	}  
26.	  
27.	void dfs(int s)  
28.	{  
29.	    dp[s][0]=val[s];  
30.	    dp[s][1]=0;  
31.	    for(int i=head[s];i!=-1;i=e[i].next)  
32.	    {  
33.	        int u=e[i].edge;  
34.	        if(v[u]==0)  
35.	        {  
36.	            v[u]=1;  
37.	            dfs(u);  
38.	            dp[s][0]+=dp[u][1];  
39.	            dp[s][1]+=max(dp[u][1],dp[u][0]);  
40.	            v[u]=0;  
41.	        }  
42.	    }  
43.	  
44.	}  
45.	  
46.	int main()  
47.	{  
48.	    int s,e;  
49.	    int ans=0;  
50.	    memset(head,-1,sizeof(head));  
51.	    memset(dp,0,sizeof(dp));  
52.	    memset(v,0,sizeof(v));  
53.	    cin>>N;  
54.	    for(int i=1;i<=N;i++)  
55.	        cin>>val[i];  
56.	    for(int i=1;i<N;i++)  
57.	    {  
58.	        cin>>s>>e;  
59.	        add(s,e);  
60.	        add(e,s);  
61.	    }  
62.	    v[1]=1;  
63.	    dfs(1);  
64.	    v[1]=0;  
65.	    ans=max(dp[1][0],dp[1][1]);  
66.	    cout<<ans<<endl;  
67.	    return 0;  
68.	}  

 

### 关于 C++ 蓝桥杯 DFS 算法题的解析 #### 一、DFS 的基本概念 深度优先搜索(Depth First Search, DFS)是一种常用的图遍历算法,可以通过递归或显式栈的方式实现。在实际编程竞赛中,DFS 经常用于解决诸如迷宫问题结构遍历以及组合枚举等问题[^1]。 以下是基于递归方式实现的一个简单 DFS 示例代码: ```cpp #include <iostream> #include <vector> using namespace std; void dfs(int node, vector<vector<int>>& adjList, vector<bool>& visited) { visited[node] = true; cout << "Visited Node: " << node << endl; // 访问当前节点 for (int neighbor : adjList[node]) { if (!visited[neighbor]) { // 如果邻居未被访问,则继续深入 dfs(neighbor, adjList, visited); } } } int main() { int n = 5; // 假设有 5 个节点 vector<vector<int>> adjList(n); // 邻接表表示图 vector<bool> visited(n, false); // 构建邻接表 adjList[0].push_back(1); adjList[0].push_back(2); adjList[1].push_back(3); adjList[1].push_back(4); // 开始 DFS dfs(0, adjList, visited); return 0; } ``` 此代码展示了一个简单的无向图上的 DFS 实现方法,其中 `adjList` 是存储图边关系的邻接表,而 `visited` 数组用来标记哪些节点已经被访问过[^4]。 --- #### 二、剪枝优化的重要性 当面对复杂问题时,单纯的 DFS 往往会因为庞大的搜索空间而导致超时或其他性能瓶颈。因此,“剪枝”成为一种重要的优化手段。通过提前判断某些分支不可能达到最优解或满足条件,可以有效减少不必要的计算量[^2]。 例如,在处理背包问题或者数独求解过程中,如果发现某条路径已经无法达成目标状态,则可以直接终止该路径下的进一步探索。 --- #### 三、具体实例分析——蓝桥杯真题改编 假设有一道典型的蓝桥杯题目如下: **描述**: 给定一棵 N 个结点构成的完全二叉,根编号为 1 。现在需要统计有多少种不同的子集能够使得这些中的叶子节点之间互不相邻。 对于此类问题,我们可以利用回溯加记忆化的方法来进行解答;同时也可以考虑适当加入一些约束条件来完成初步筛工作以加快速度。 下面是针对上述场景设计的一份伪代码框架供参考: ```cpp bool isValid(vector<int>& selectedLeaves){ /* 判断所出的叶节点集合是否合法 */ } long long countWays(int currentLevel,int lastPickedIndex,vector<int>& candidates){ if(currentLevel > maxLevels){return candidate.size()==requiredSize;} long long res=0; for(auto nextOption:candidates){ if(!isValidSelection(lastPickedIndex,nextOption))continue;//Pruning Step //Make choice selected.push_back(nextOption); //Explore further levels res +=countWays(currentLevel+1 ,nextOption,candidates); //Undo Choice selected.pop_back(); } return res; } ``` 这里的关键在于每次做出选择之前都要先验证其可行性(`!isValidSelection`),这一步骤正是所谓的“剪枝”。 --- ### 总结 综上所述,掌握好基础版的递归形式 DFS 同样重要的是学会根据不同情况灵活运用各种技巧对其进行改进提升效率比如引入剪枝机制等等][^[^24]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值