LCCUP 力扣杯 二叉树染色

2. 二叉树染色

目录

2. 二叉树染色

题目

思路

问题

大佬代码:

总结:


题目


小扣有一个根结点为 root 的二叉树模型,初始所有结点均为白色,可以用蓝色染料给模型结点染色,模型的每个结点有一个 val 价值。小扣出于美观考虑,希望最后二叉树上每个蓝色相连部分的结点个数不能超过 k 个,求所有染成蓝色的结点价值总和最大是多少?
 

示例1:

输入:root = [5,2,3,4], k = 2

输出:12

解释:结点 5、3、4 染成蓝色,获得最大的价值 5+3+4=12

 

示例2:

输入:root = [4,1,3,9,null,null,2], k = 2

输出:16

解释:结点 4、3、9 染成蓝色,获得最大的价值 4+3+9=16

 

 提示:

  • 1 <= k <= 10
  • 1 <= val <= 10000
  • 1 <= 结点数量 <= 10000

 

思路

首先先用暴力破解的方式去尝试:

每次遇到一个节点的时候,对该节点的操作有两种选择。第一种选择选择当前节点,即root节点,还有一个选择就是不选择root节点。那么当前节点的值就有两种:

第一种是选择root节点,然后左右子节点与root相连不超过k的最大值是什么。但是就要讨论,root节点分配之后,k值少1,即左节点与root节点相连的个数右节点与root相连的个数之和不超过k - 1。然后在对左右节点调用dfs的值的时候,将k值设置成限制的值。

第二种就是不选择root节点,左右两个节点都可以单独看作成一个树来讨论问题,因为root节点不选择,所以左右两个子节点之间没有任何关系,而当前的最大值则变成了左节点的最大值加上右节点的最大值之和。递归调用的时候k值直接设置成题目要求。

然后比较这两种选择之后返回最大值。其实就是一个递归问题。

 

暴力破解算法如下:

int dfs(TreeNode* root, int k){
    if(!root) return 0;
    
    int sum = 0;
    // 选择root节点的循环
    if(k > 0){
        for(int i = 0; i < k; i++){
            int left = dfs(root -> left, i);
            int right = dfs(root -> right, k - 1- i);
            sum = max(sum, left + right + root -> val);    // 储存最大值
        }
    }
    
    // 不选择root节点的情况
    int left = dfs(root -> left, k);
    int right = dfs(root -> right, k);
    sum = max(sum, left + right);    // 比较选择与不选择之间谁大
    return sum;
}
        

 

问题

但是很明显,就是递归的时候,会反复计算重复的值,所以,即是递归,又有重复子问题,那么想到什么??

动态规划!!

那么我们只要保存好当前节点在当前k情况下的值即可。然后在访问的时候就直接查备忘录。

class Solution {
public:
    int _k;
    unordered_map<TreeNode *, int> map;
    int count = 0;
    int dp[10002][11] = {{-1}};    // 第一个是节点索引的映射,第二个是相对应的K值
 
    int maxValue(TreeNode* root, int k) {
        _k = k;
        memset(dp, -1, sizeof(dp));    // 开辟备忘录
        return dfs(root, k);
    }
    
    int dfs(TreeNode* root, int k){
        if(!root) return 0;
        
        // 若不在备忘录内,则写入备忘录
        if(map.find(root) == map.end()){
            map.insert(make_pair(root, count));
            count++;
        }
        
        int idx = map[root];
        int sum = 0;
        
        if(dp[idx][k] != -1) return dp[idx][k];    // 如果备忘录有,则获取
        
        // 选择root节点的情况
        for(int i = 0 ; i < k; i++){
            int left = dfs(root -> left, i);        
            int right = dfs(root -> right, k - 1 - i);
            sum = max(sum, left + right + root -> val);
        }
        
        // 不选择root节点的情况
        int nosum = dfs(root -> left, _k) + dfs(root -> right, _k);
        sum = max(sum, nosum);
        
        dp[idx][k] = sum;    // 将最大值储存在备忘录内,该节点在K值情况的最大值
        
        return sum;
    }
    
};

 

大佬代码:

比赛结束之后看了高手的题,简介明了,我可以相对的解释一下。

代码如下

class Solution {
public:
	vector<int> dfs(TreeNode* root, int k){
		if (root==NULL){
			vector<int> res;
			res.push_back(0);    // 返回数组,代表为空的时候,即有一个0
			return res;
		}
		vector<int> vl=dfs(root->left ,k);
		vector<int> vr=dfs(root->right ,k);
		vector<int> res;
		res.resize(k+1);        // 值最大为k但是数组从0开始算
		for (int i=0;i<vl.size();i++)
			for (int j=0;j<vr.size();j++){
				res[0]=max(res[0],vl[i]+vr[j]);    // 不选择当前节点的最大值
				if (i+j+1<=k)
					res[i+j+1]=max(res[i+j+1],vl[i]+vr[j]+root->val);    // 选择当前节点的最大值
			}
		return res;
	}
    int maxValue(TreeNode* root, int k) {
		vector<int> result=dfs(root,k);
		int ans=0;
		for (int i=0;i<=result.size();i++)
			if (i<=k) ans=max(ans,result[i]);    // 比较所有可能性
		return ans;
    }
};

dfs每次都会返回一个数组,数组代表的是{0, 1, 2 ... k}。

数组的索引代表的是与该节点相连的点的个数。比如返回一个 vl 为{ 4 ,2, 6 }。代表与左节点相连为0个的最大值为4(即不选择左节点时,左节点的最大值)。第二个为与左节点相连的点的个数为1的最大值(其实就是只选了左节点,然后左节点的子节点与左节点断开连接)。以此类推。

if (i+j+1<=k)
      res[i+j+1]=max(res[i+j+1],vl[i]+vr[j]+root->val);

而这一个代表的是,选择root节点和i个左节点和j个右节点,总数不超过k的最大值。

 

总结:

大佬的代码太简洁了,我还得多加学习。

第一次写这个,如果题目侵权的话就删删。

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值