【洛谷 P1137 旅行计划】简单树形DP

本文介绍了一种使用树形动态规划(DP)解决特定图论问题的方法:给定一个有向无环图,求每个点作为终点时所能达到的最长链的长度。通过实例解释了如何将问题转化为DP形式,并给出了具体的C++实现代码。

题目链接:
https://www.luogu.org/problemnew/show/P1137
题意:
给你 N N N 个点, M M M 条边,所有边都是单向边而且不存在环路,求出以点 i i i 为终点能够经过的最长链点数。
题解:
如果是第一次接触树形DP,不妨先从简单的情况开始考虑,比如说就是一条直线:1->2->3->4, 现在记 F [ i ] F[i] F[i] 为以 i i i 为终点所经过的最长链点数,那么显然, F [ 4 ] = 1 F[4] = 1 F[4]=1,点4只有它本身, F [ 3 ] = 2 F[3] = 2 F[3]=2 F [ 2 ] = 3 F[2] = 3 F[2]=3, F [ 1 ] = 4 F[1] = 4 F[1]=4,不难想出, F [ t o ] = F [ f r o m ] + 1 F[to] = F[from] + 1 F[to]=F[from]+1。那么现在延伸一下,把他扩展到更复杂的图中。在复杂的图中,一个点可能与多个点相连,但是我们仍有 F [ t o ] = F [ f r o m ] + 1 F[to] = F[from] +1 F[to]=F[from]+1, 不过别忘了, F [ i ] F[i] F[i] 表示的是以 i i i 为终点所经过的最长链点数,要取最大值,因为有的路过来长,有的路过来短,所以 F [ t o ] = m a x ( F [ t o ] , F [ f r o m ] + 1 ) F[to] = max(F[to], F[from] + 1) F[to]=max(F[to],F[from]+1),也就是父节点从所有子节点里面选最大的那个。
代码:

#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
#define pi 3.1415926
#define mp(x, y) make_pair(x, y)
#define vi vector<int>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> Pair;
const int MAX = 1e5 + 10;

struct edge {
	int nxt, to;
}e[MAX << 1];

int N, M;
int f[MAX];// F[i]为以i为终点所经过的最长链点数
int head[MAX], tot;//前向星

void add(int u, int v) {//前向星加边
	e[++tot].nxt = head[u];
	e[tot].to = v;
	head[u] = tot;
}

void dfs(int u) {//当前点为u
	if (f[u])return;//已经算过
	int flag = 0;//标记是否还有子节点
	for (int i = head[u]; i; i = e[i].nxt) {
		flag = 1;
		int son = e[i].to;
		dfs(son);
		f[u] = max(f[u], f[son] + 1);
	}
	if (!flag)f[u] = 1;
}

int main() {
	ios::sync_with_stdio(0);
	tot = 0;
	cin >> N >> M;
	for (int i = 0; i < M; i++) {
		int x, y;
		cin >> x >> y;
		add(y, x);
	}
	for (int i = 1; i <= N; i++) {
		dfs(i);
		cout << f[i] << endl;
	}
	return 0;
}

### 洛谷 P3323 题目描述 洛谷 P3323 是一道涉及图论和数论的题目,题目的大致描述如下: 给定一个长度为 $ n $ 的正整数序列 $ a_1, a_2, \ldots, a_n $。需要从中选出若干个区间(可以不连续),使得每个区间的元素满足某种条件。具体来说,对于每个选中的区间 $[l, r]$,要求其内部的所有元素构成一个合法的环状结构,并且满足某些特定的数学关系。 最终的目标是计算出能够选出的符合条件的区间数量或总权重的最大值。 由于该题具有较高的复杂性,通常需要结合图论、动态规划以及数学推导等多种算法思想来解决[^6]。 ### 解题思路 #### 1. **问题分析** - 题目中涉及的“合法环状结构”通常意味着在某个模数意义下存在同余关系。 - 序列中的每个元素可能代表图中的节点,而区间的选择可能对应于图中某条路径的存在性。 - 因此,解题的关键在于如何将原始问题转化为图上的可达性问题或者最短路问题。 #### 2. **建模与转化** - 可以将每个位置 $ i $ 看作图中的一个节点。 - 如果某个区间 $[i, j]$ 满足条件,则可以在节点 $ i $ 和节点 $ j+1 $ 之间建立一条有向边,表示可以从 $ i $ 转移到 $ j+1 $。 - 这样,原问题就转化为在这张图中从起点到终点的路径计数问题或最大权值路径问题。 #### 3. **算法选择** - 使用 **前缀异或** 或 **前缀模运算** 来快速判断某个区间是否满足环形条件。 - 构造一个哈希表或字典,记录当前处理过程中出现过的前缀状态,以便快速查找是否存在满足条件的区间。 - 对于最终的路径统计部分,可以使用 **动态规划**: - 设 $ dp[i] $ 表示以第 $ i $ 个位置为结尾的最优解。 - 状态转移方程可以设计为: $$ dp[i] = \max(dp[j] + w) \quad \text{其中 } [j+1, i] \text{ 是一个合法区间} $$ #### 4. **实现细节** - 使用哈希结构维护当前有效的前缀状态。 - 动态规划时注意初始化边界条件。 - 若题目要求输出方案数,需额外维护一个计数数组。 #### 5. **时间复杂度优化** - 初始暴力做法的时间复杂度可能是 $ O(n^2) $,但通过哈希优化可以降低到 $ O(n) $ 或 $ O(n \log n) $。 --- ### 示例代码(伪代码) ```cpp // 假设 pre_hash 表示前缀状态,dp 表示动态规划数组 unordered_map<int, vector<int>> hash_map; hash_map[0].push_back(0); // 初始状态 for (int i = 1; i <= n; ++i) { int current_state = compute_prefix_state(i); for (int j : hash_map[current_state]) { dp[i] = max(dp[i], dp[j] + weight(j+1, i)); } hash_map[current_state].push_back(i); } ``` ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值