8.25

今日补题:
发现自身:
1.树形、图DP完全不行(可能是因为对树和图本身就不熟)
于是上OI wiki学习了一番

感悟
一定要从例题入手,写一遍胜过看百遍
通过经典的"二叉树最大路径和"问题。可以探究设计状态表示和状态转移的一般方式。

struct TreeNode {
int val;
TreeNode* left;
TreeNode* right;
TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
};

int maxPathSum(TreeNode* root) {
int maxSum = INT_MIN;
dfs(root, maxSum);
return maxSum;
}

int dfs(TreeNode* node, int& maxSum) {
if (!node) return 0;

// 递归计算左右子树的最大贡献值
int leftMax = max(dfs(node->left, maxSum), 0);
int rightMax = max(dfs(node->right, maxSum), 0);

// 计算当前节点为根的最大路径和
int priceNewpath = node->val + leftMax + rightMax;

// 更新全局最大值
maxSum = max(maxSum, priceNewpath);

// 返回当前节点的最大贡献值
return node->val + max(leftMax, rightMax);

}

树形DP的核心:后序遍历和状态定义。我们需要先处理子节点,再利用子节点的结果计算当前节点的状态。

经典问题:树上的背包问题
这类问题通常需要记录以每个节点为根的子树中,选择一定数量的节点所能获得的最优值。
vector<vector> dp(n, vector(k + 1, 0));

void treeDP(int u, int parent) {
// 初始化,选择当前节点
dp[u][1] = value[u];

for (int v : tree[u]) {
    if (v == parent) continue;
    
    treeDP(v, u);  // 先递归处理子节点
    
    // 背包转移:从大到小遍历,避免重复选择
    for (int i = k; i >= 1; --i) {
        for (int j = 0; j <= i - 1; ++j) {
            dp[u][i] = max(dp[u][i], dp[u][i - j] + dp[v][j]);
        }
    }
}

}

树形DP的本质是将树分解为子树问题,再组合结果。我们需要仔细设计状态转移方程,考虑如何将子节点的状态合并到当前节点。

心得与建议

对于和我一样的新手,我有以下几点建议:

  1. 从简单二叉树问题开始,逐步过渡到多叉树问题
  2. 先理解递归过程,再思考状态设计和转移
  3. 多画图,可视化树结构和DP状态转移
  4. 掌握几种常见模型:最大独立集、最小支配集、树上背包等
  5. 重视边界条件,特别是叶子节点的处理

明天再多加练习。

概率与期望
!!!!期望往往会出现小数,一定要注意精度的处理
(建议每一步都做处理,不要到最后再一起处理)

posted @ 2025-08-25 23:46  glad_yimie  阅读(9)  评论(0)    收藏  举报