2023-09-21力扣每日一题

链接:

2603. 收集树中金币

题意

一个无向无根树,每个点上有Coins[T]个金币,每次你可以选择吸收离自己距离小于等于2的节点的所有金币,也可以移动到相邻格子上,求全部吸完最小的移动次数(任选起点,但吸完要返回起点)

由于计算的是移动次数,所以你可以认为一直在吸,走到哪儿吸到哪儿

计算最后移动次数很简单,由于要回到起点,所以移动次数一定是(n-1)*2-当n>0时,因为两个点之间要经过两次(来回且无环)

根据度,我们可以移除两次,度为0和1的节点,这些节点的特点是可以看做叶子节点,由于我们可以直接吸距离2的所有金币,所以我们在父节点不需要向下移动,就可以处理掉这些叶子结点。要注意的是,做这一步时,要等所有叶子结点都挑出来以后再更新度,以免提前计算将要成为叶子节点的节点

同时要记得先把所有没金币的叶子节点去掉,这边要注意和上面不同的是,即时更新度,要把新的叶子结点中没金币的也去掉(我偷懒了,大循环了)

实际代码:

#include<bits/stdc++.h>
using namespace std;
int collectTheCoins(vector<int>& coins, vector<vector<int>>& edges)
{
    int lg=coins.size();
	if(lg<=5) return 0;
		
    vector<vector<int>>Map(lg);
    vector<bool>book(lg);
    
    //边结构 
    for(auto edge:edges)
    {
    	//if(coins[edge[0]]==0 || coins[edge[1]]==0) continue;
    	Map[edge[0]].push_back(edge[1]);
    	Map[edge[1]].push_back(edge[0]);
	}
	

	while(true)
	{
		bool kill=0;
		for(int j=0;j<lg;j++)
		{
			if(book[j]) continue;
			int du=0;
			for(auto edge:Map[j])
			{
				if(!book[edge]) du++;
			}
			//及时更新 
			if(du<=1 && coins[j]==0)
			{
				book[j]=1;
				kill=1;
			}
		}
		//全部空金币叶子结点移除 
		if(!kill) break;
	}
	
	//两层移除 
	for(int i=0;i<2;i++)
	{
		vector<int>True;
		for(int j=0;j<lg;j++)
		{
			if(book[j]) continue;
			int du=0;
			for(auto edge:Map[j])
			{
				if(!book[edge]) du++;
			}
			if(du<=1)
			{
				True.push_back(j);
			}
		}
		//延后更新
		for(auto T:True) book[T]=1;
	}
	//计算最后移动次数 
	int last=0;
	for(auto bk:book) 
	{
		if(!bk) last++;
	}

	return last?(last-1)*2:0;
}

限制:

  • n == coins.length
  • 1 <= n <= 3 * 104
  • 0 <= coins[i] <= 1
  • edges.length == n - 1
  • edges[i].length == 2
  • 0 <= ai, bi < n
  • ai != bi
  • edges 表示一棵合法的树。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值