0
点赞
收藏
分享

微信扫一扫

2023-09-21力扣每日一题

船长_Kevin 2023-09-23 阅读 46

链接:

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 表示一棵合法的树。
举报

相关推荐

0 条评论