树上dp问题

给定一棵n个节点的有根树,节点编号范围为1∼n,其中根节点编号为1。现在你需要给树上每个节点赋值左括号(或者右括号),问你有多少种赋值方案,使得赋值完毕后从根节点到达每一个叶子节点的唯一简单路径所经过的点构成的括号串是一个合法括号串(求出方案数对998244353取模的结果)?

我们递归的定义合法括号串:

  • 空括号串是合法的。
  • A合法,则(A)合法。
  • AB合法,则AB合法。
格式

输入格式:

第一行一个整数T(1≤T≤500),表示测试数据组数,对于每组测试数据:

第一行一个整数n(1≤n≤3000)。

接下来n−1n−1行,每行两个整数u,v(1≤u,v≤n),表示点uu与点vv之间有一条边。

保证单个测试点的所有组测试数据的nn之和不超过3000。

输出格式:

对于每组测试数据:

输出一行一个整数,表示答案。

样例 1

输入:

复制代码
1
4
1 2
2 3
3 4

复制

输出:

复制代码
2

复制

样例 2

输入:

复制代码
1
11
1 2
1 3
1 4
2 5
2 8
3 10
5 6
5 7
8 9
10 11

复制

输出:

复制代码
4

复制

备注

对于样例11,这是一条链,合法的括号串有()()(())两种,答案为2。

#include <iostream>

#include <vector>

using namespace std;

const int MOD = 998244353;

vector<int> adj[3005];

long long dp[3005][3005];//来到u节点前,欠j个左括号,到根后能形成的合法方案数

int n;

void dfs(int u, int p) {

// 1. 先递归处理子节点

vector<int> children;

for (int v : adj[u]) {

if (v != p) {

children.push_back(v);

dfs(v, u);

}

}

// 2. 计算当前节点在不同初始余额 j 下的方案数

// j 是"进入节点 u 之前"的余额

for (int j = 0; j <= n; ++j) {

long long ways_as_L = 0;

long long ways_as_R = 0;

// 情况 A: 节点 u 填左括号 '('

// 填完后余额变为 j + 1

if (j + 1 <= n) {

if (children.empty()) {

// 如果 u 是叶子,填 '(' 后余额为 j+1

// 路径结束要求余额必须为 0,所以 j+1 == 0 (不可能)

ways_as_L = 0;

} else {

ways_as_L = 1;

for (int v : children) {

ways_as_L = (ways_as_L * dp[v][j + 1]) % MOD;

}

}

}

// 情况 B: 节点 u 填右括号 ')'

// 填完后余额变为 j - 1

if (j - 1 >= 0) {

if (children.empty()) {

// 如果 u 是叶子,填 ')' 后余额为 j-1

// 路径结束要求余额必须为 0,即 j 必须是 1

ways_as_R = (j - 1 == 0 ? 1 : 0);

} else {

ways_as_R = 1;

for (int v : children) {

ways_as_R = (ways_as_R * dp[v][j - 1]) % MOD;

}

}

}

dp[u][j] = (ways_as_L + ways_as_R) % MOD;

}

}

void solve() {

cin >> n;

for (int i = 1; i <= n; i++) {

adj[i].clear();

for (int j = 0; j <= n; j++) dp[i][j] = 0;

}

for (int i = 0; i < n - 1; i++) {

int u, v;

cin >> u >> v;

adj[u].push_back(v);

adj[v].push_back(u);

}

// 题目要求根节点到叶子的路径。根节点编号为 1。

dfs(1, 0);

// 最终答案是:进入根节点 1 之前,余额为 0 的方案数

cout << dp[1][0] << endl;

}

int main() {

ios::sync_with_stdio(false);

cin.tie(0);

int T;

cin >> T;

while (T--) {

solve();

}

return 0;

}

相关推荐
自我意识的多元宇宙2 小时前
【数据结构】 散列表
数据结构·散列表
无籽西瓜a2 小时前
MD5算法原理、适用场景
java·后端·算法·哈希算法·md5
承渊政道2 小时前
【动态规划算法】(简单多状态dp问题入门与经典题型解析)
数据结构·c++·学习·算法·leetcode·macos·动态规划
fie88892 小时前
免疫优化算法在物流配送中心选址中的应用
算法·数学建模
王老师青少年编程2 小时前
csp信奥赛C++高频考点专项训练之贪心算法 --【部分背包问题】:部分背包问题
c++·算法·贪心·csp·信奥赛·部分背包问题
九思十安2 小时前
HNU2026-算法设计与分析-笔记 3 摊还分析
笔记·算法
嘻嘻哈哈樱桃2 小时前
牛客经典101题题解集--哈希
java·数据结构·python·算法·leetcode·职场和发展·哈希算法
自我意识的多元宇宙2 小时前
【数据结构】 红黑树
数据结构·算法
wayz112 小时前
Day 15 编程实战:KMeans聚类与股票风格分类
算法·机器学习·分类·kmeans·聚类