首页 最新 热门 推荐

  • 首页
  • 最新
  • 热门
  • 推荐

【图论】【树】 【拓扑排序】2603. 收集树中金币

  • 25-02-22 05:20
  • 4284
  • 9201
blog.csdn.net

本文涉及知识点

图论 树 拓扑排序

LeetCode 2603. 收集树中金币

给你一个 n 个节点的无向无根树,节点编号从 0 到 n - 1 。给你整数 n 和一个长度为 n - 1 的二维整数数组 edges ,其中 edges[i] = [ai, bi] 表示树中节点 ai 和 bi 之间有一条边。再给你一个长度为 n 的数组 coins ,其中 coins[i] 可能为 0 也可能为 1 ,1 表示节点 i 处有一个金币。

一开始,你需要选择树中任意一个节点出发。你可以执行下述操作任意次:

收集距离当前节点距离为 2 以内的所有金币,或者
移动到树中一个相邻节点。
你需要收集树中所有的金币,并且回到出发节点,请你返回最少经过的边数。

如果你多次经过一条边,每一次经过都会给答案加一。

示例 1:
在这里插入图片描述

输入:coins = [1,0,0,0,0,1], edges = [[0,1],[1,2],[2,3],[3,4],[4,5]]
输出:2
解释:从节点 2 出发,收集节点 0 处的金币,移动到节点 3 ,收集节点 5 处的金币,然后移动回节点 2 。
示例 2:

在这里插入图片描述

输入:coins = [0,0,0,1,1,0,0,1], edges = [[0,1],[0,2],[1,3],[1,4],[2,5],[5,6],[5,7]]
输出:2
解释:从节点 0 出发,收集节点 4 和 3 处的金币,移动到节点 2 处,收集节点 7 处的金币,移动回节点 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 表示一棵合法的树。

拓扑排序

利用拓扑排序不断删除没有金币的叶子节点,不影响答案。
如果根节点没有金币且只有一个孩子,删除。它的孩子成为新的根。简便方式是直接选择有金币的节点为根。其实,拓扑排序后,不会存在这样的根。
一个子树cur有若干金币,且叶子节点的最早公共祖先是cur。则收集所有金币只需要:移动到叶子祖父节点,再回来。
如果cur是整个树的根,则收集结束。等于:除非叶子节点、非叶子节点父亲外所有节点 × \times ×*2。
如果不是根,cur 需要通过根收集金币,所以cur → \rightarrow → root 路径上的边,也要来回一次。
对于任意cur,cur → \rightarrow → parent 和parent → \rightarrow → cur都只需要执行一次。
因为一次就可以收集所有金币。

题解

拓扑排序删除非金币节点后。

错误解法

DFS,返回cur距离叶子最远距离,如果>=2 ,则m_iRet+=2。注意:根节点不算,它没有父节点。DFS的时候排除已经被拓扑排序删除的节点。叶子节点到叶子节点的距离是0。果没有金币,直接返回0。错误原因:不同的根节点,结果不一样。

正确解法

删除叶子节点(无论是否有金币)两次。如果余下的节点为0,则返回0,否则返回( 剩余节点数-1) × \times × 2

代码

核心代码

class CTopSort
{
public:	
	template <class T = vector<int> >
	void Init(const vector<T>& vNeiBo,bool bDirect = true )
	{
		const int iDelOutDeg = bDirect ? 0 : 1;
		m_c = vNeiBo.size();
		m_vBackNeiBo.resize(m_c);
		vector<int> vOutDeg(m_c);
		for (int cur = 0; cur < m_c; cur++)
		{
			vOutDeg[cur] = vNeiBo[cur].size();	
			for (const auto& next : vNeiBo[cur])
			{
				m_vBackNeiBo[next].emplace_back(cur);
			}
		}
		vector<bool> m_vHasDo(m_c);
		queue<int> que;
		for (int i = 0; i < m_c; i++)
		{
			if ( vOutDeg[i] <= iDelOutDeg)
			{
				m_vHasDo[i] = true;
				if (OnDo(i)) {
					que.emplace(i);
				}
			}
		}

		while (que.size())
		{
			const int cur = que.front();
			que.pop();
			for (const auto& next : m_vBackNeiBo[cur])
			{
				if (m_vHasDo[next] )
				{
					continue;
				}				
				vOutDeg[next]--;
				if (vOutDeg[next] <= iDelOutDeg )
				{
					m_vHasDo[next] = true;
					if (OnDo(next)) {
						que.emplace(next);
					}
				}
			}
		};
	}
	int m_c;
protected:
	virtual bool OnDo( int cur) = 0;
	vector<vector<int>> m_vBackNeiBo;	
	
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58

测试用例

template<class T, class T2>
void Assert(const T& t1, const T2& t2)
{
	assert(t1 == t2);
}

template<class T>
void Assert(const vector<T>& v1, const vector<T>& v2)
{
	if (v1.size() != v2.size())
	{
		assert(false);
		return;
	}
	for (int i = 0; i < v1.size(); i++)
	{
		Assert(v1[i], v2[i]);
	}

}

int main()
{
	vector<int> coins;
	vector<vector<int>> edges;
	{
		Solution sln;
		coins = { 1,0,1,1,1,1,0,0,0,0,0,0,0,1,1,1,0,0 }, edges = { {0,1},{0,2},{1,3},{2,4},{2,5},{3,6},{3,7},{4,8},{7,9},{8,10},{10,11},{11,12},{7,13},{8,14},{9,15},{11,16},{13,17} };
		auto res = sln.collectTheCoins(coins, edges);
		Assert(10, res);
	}
	{
		Solution sln;
		coins = { 1,0,0,1,1,0,0,0,0,1,0,0 }, edges = { {0,1},{1,2},{1,3},{2,4},{4,5},{5,6},{5,7},{4,8},{7,9},{7,10},{10,11} };
		auto res = sln.collectTheCoins(coins, edges);
		Assert(4, res);
	}
	{
		Solution sln;
		coins = { 1,0,0,0,0,1 }, edges = { {0,1},{1,2},{2,3},{3,4},{4,5} };
		auto res = sln.collectTheCoins(coins, edges);
		Assert(2, res);
	}
	{
		Solution sln;
		coins = { 0,0,0,1,1,0,0,1 }, edges = { {0,1},{0,2},{1,3},{1,4},{2,5},{5,6},{5,7} };
		auto res = sln.collectTheCoins(coins, edges);
		Assert(2, res);
	}	
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50

2023年4月

class Solution {
public:
int collectTheCoins(vector& coins, vector& edges) {
m_c = coins.size();
vector vDeg(m_c);
vector vNeiB(m_c);
for (const auto& v : edges)
{
vNeiB[v[0]].emplace_back(v[1]);
vNeiB[v[1]].emplace_back(v[0]);
vDeg[v[0]]++;
vDeg[v[1]]++;
}
std::queue que;
for (int i = 0; i < m_c; i++)
{
if ((0 == coins[i]) && (1 == vDeg[i]))
{
que.emplace(i);
}
}
//根据Top排序,依次删除没有金币的节点,自己和子节点没有金币的节点, 自己和子孙节点没金币的节点…
while (que.size())
{
int iCur = que.front();
que.pop();
for (const auto& next : vNeiB[iCur])
{
vDeg[next]–;
if ((0 == coins[next]) && (1 == vDeg[next]))
{
que.emplace(next);
}
}
vNeiB[iCur].clear();
}
//通过top排序生成平衡树,Dis为0,表示页节点;1表示离最近的叶节点为1
//访问所有完所有Dis为2的节点,可以收集所有金币
//每个叶子只有一个父节点,自然只有一个祖父节点。必须访问所有Dis为2的节点,否则它子孙的金币无法收集
//
//叶节点入队
vector vDis(m_c, 0);
for (int i = 0; i < m_c; i++)
{
if (1 == vDeg[i])
{
que.emplace(i);
}
}
if (que.size() <=1)
{
return 0;
}
while (que.size())
{
const int iCur = que.front();
que.pop();
for (const auto& next : vNeiB[iCur])
{
vDeg[next]–;
if (1 == vDeg[next])
{
que.emplace(next);
vDis[next] = vDis[iCur] + 1;
}
}
vNeiB[iCur].clear();
}
//
int iRet = -2;
for (int i = 0; i < m_c; i++)
{
if (vDis[i] >= 2)
{
iRet += 2;
}
}
return max(0,iRet);
}
int m_c;
};

2023年8月

class Solution {
public:
int collectTheCoins(vector& coins, vector& edges) {
m_c = coins.size();

	CNeiBo2 neiBo(m_c, edges, false);
	vector vDeg(m_c);
	for (const auto& v : edges)
	{
		vDeg[v[0]]++;
		vDeg[v[1]]++;
	}
	std::queue que,queCoins;//非金币叶子 金币叶子
	vector vDel(m_c);
	for (int i = 0; i < m_c; i++)
	{
		if (1 == vDeg[i])
		{
			if (0 == coins[i])
			{
				que.emplace(i);					
			}
			else
			{
				queCoins.emplace(i);
			}
		}
	}

	while (que.size())
	{
		const int cur = que.front();
		que.pop();
		vDel[cur] = true;
		for (const auto& next : neiBo.m_vNeiB[cur])
		{
			vDeg[next]--;
			if (1 == vDeg[next])
			{
				if (0 == coins[next])
				{
					que.emplace(next);
				}
				else
				{
					queCoins.emplace(next);
				}
			}
		}
	}

	queue queParent;
	while (queCoins.size())
	{
		const int cur = queCoins.front();
		queCoins.pop();
		vDel[cur] = true;
		for (const auto& next : neiBo.m_vNeiB[cur])
		{
			vDeg[next]--;
			if (1 == vDeg[next])
			{
				queParent.emplace(next);
				vDel[next] = true;
			}
		}
	}

	int iRet = 0;
	for (const auto& v : edges)
	{
		if (vDel[v[0]] || vDel[v[1]])
		{
			continue;
		}
		iRet += 2;
	}
	return iRet;
}
int m_c;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75

};

扩展阅读

视频课程

有效学习:明确的目标 及时的反馈 拉伸区(难度合适),可以先学简单的课程,请移步CSDN学院,听白银讲师(也就是鄙人)的讲解。
https://edu.csdn.net/course/detail/38771

如何你想快

速形成战斗了,为老板分忧,请学习C#入职培训、C++入职培训等课程
https://edu.csdn.net/lecturer/6176

相关

下载

想高屋建瓴的学习算法,请下载《喜缺全书算法册》doc版
https://download.csdn.net/download/he_zhidan/88348653

我想对大家说的话
闻缺陷则喜是一个美好的愿望,早发现问题,早修改问题,给老板节约钱。
子墨子言之:事无终始,无务多业。也就是我们常说的专业的人做专业的事。
如果程序是一条龙,那算法就是他的是睛

测试环境

操作系统:win7 开发环境: VS2019 C++17
或者 操作系统:win10 开发环境: VS2022 C++17
如无特殊说明,本算法用**C++**实现。

文章知识点与官方知识档案匹配,可进一步学习相关知识
算法技能树首页概览60496 人正在系统学习中
群中有博文配套源码
QQ群名片
注:本文转载自blog.csdn.net的闻缺陷则喜何志丹的文章"https://blog.csdn.net/he_zhidan/article/details/136917243"。版权归原作者所有,此博客不拥有其著作权,亦不承担相应法律责任。如有侵权,请联系我们删除。
复制链接
复制链接
相关推荐
发表评论
登录后才能发表评论和回复 注册

/ 登录

评论记录:

未查询到任何数据!
回复评论:

分类栏目

后端 (14832) 前端 (14280) 移动开发 (3760) 编程语言 (3851) Java (3904) Python (3298) 人工智能 (10119) AIGC (2810) 大数据 (3499) 数据库 (3945) 数据结构与算法 (3757) 音视频 (2669) 云原生 (3145) 云平台 (2965) 前沿技术 (2993) 开源 (2160) 小程序 (2860) 运维 (2533) 服务器 (2698) 操作系统 (2325) 硬件开发 (2492) 嵌入式 (2955) 微软技术 (2769) 软件工程 (2056) 测试 (2865) 网络空间安全 (2948) 网络与通信 (2797) 用户体验设计 (2592) 学习和成长 (2593) 搜索 (2744) 开发工具 (7108) 游戏 (2829) HarmonyOS (2935) 区块链 (2782) 数学 (3112) 3C硬件 (2759) 资讯 (2909) Android (4709) iOS (1850) 代码人生 (3043) 阅读 (2841)

热门文章

101
推荐
关于我们 隐私政策 免责声明 联系我们
Copyright © 2020-2025 蚁人论坛 (iYenn.com) All Rights Reserved.
Scroll to Top