收集所有金币可获得的最大积分

节点 i 上的金币可以用下述方法之一进行收集:
收集所有金币,得到共计 coins[i] - k 点积分。如果 coins[i] - k 是负数,你将会失去 abs(coins[i] - k) 点积分。
收集所有金币,得到共计 floor(coins[i] / 2) 点积分。如果采用这种方法,节点 i 子树中所有节点 j 的金币数 coins[j] 将会减少至 floor(coins[j] / 2) 。
返回收集所有树节点的金币之后可以获得的最大积分

1. 动态规划树

由于每个节点有两种选择,导致后面会有重复的状态,采用动态规划
相比打家劫舍的偷与不偷的两个状态,该题每个节点存在更多状态,所以采用的动态规划维度更大,这里设置32
动态规划就是为了减少重复计算

class Solution {
public:
    int maximumPoints(vector<vector<int>> &edges, vector<int> &coins, int k) {
        int n = coins.size();
        vector<vector<int>> g(n);//create graph
        for (auto &e: edges) {
            int x = e[0], y = e[1];
            g[x].push_back(y);
            g[y].push_back(x);
        }

        vector<vector<int>> memo(n, vector<int>(32, INT_MIN)); // 第i个节点位于第j层时子树的最大积分
        function<int(int, int, int)> dfs = [&](int i, int j, int fa) -> int {//第i个节点位于第j层时子树的最大积分
            auto &res = memo[i][j]; // 注意这里是引用
            if (res != INT_MIN) // 之前计算过
                return res;
            int res1 = (coins[i] >> j) - k;//第一种方法
            int res2 = coins[i] >> (j + 1);//第二种方法
            for (int ch: g[i]) {//遍历子节点
                if (ch == fa) continue;//跳过父节点
                res1 += dfs(ch, j, i); // 第一种方法子节点的情况
                if(j<30)
                    res2 += dfs(ch, j + 1, i); // 第二种方法子节点的情况
            }
            return res = max(res1, res2); // 记忆化
        };
        return dfs(0, 0, -1);//从根结点开始,深度为0,无前驱
    }
};
posted @ 2023-10-31 23:36  失控D大白兔  阅读(25)  评论(0编辑  收藏  举报