首页 最新 热门 推荐

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

【树】【图论】【树路径】【深度优先搜索】2867. 统计树中的合法路径数目

  • 25-02-22 04:41
  • 4750
  • 6366
blog.csdn.net

作者推荐

【广度优先搜索】【网格】【割点】【 推荐】1263. 推箱子

本文涉及知识点

树 图论 深度优先搜索

LeetCode:2867. 统计树中的合法路径数目

给你一棵 n 个节点的无向树,节点编号为 1 到 n 。给你一个整数 n 和一个长度为 n - 1 的二维整数数组 edges ,其中 edges[i] = [ui, vi] 表示节点 ui 和 vi 在树中有一条边。
请你返回树中的 合法路径数目 。
如果在节点 a 到节点 b 之间 恰好有一个 节点的编号是质数,那么我们称路径 (a, b) 是 合法的 。
注意:
路径 (a, b) 指的是一条从节点 a 开始到节点 b 结束的一个节点序列,序列中的节点 互不相同 ,且相邻节点之间在树上有一条边。
路径 (a, b) 和路径 (b, a) 视为 同一条 路径,且只计入答案 一次 。
示例 1:
输入:n = 5, edges = [[1,2],[1,3],[2,4],[2,5]]
输出:4
解释:恰好有一个质数编号的节点路径有:

  • (1, 2) 因为路径 1 到 2 只包含一个质数 2 。
  • (1, 3) 因为路径 1 到 3 只包含一个质数 3 。
  • (1, 4) 因为路径 1 到 4 只包含一个质数 2 。
  • (2, 4) 因为路径 2 到 4 只包含一个质数 2 。
    只有 4 条合法路径。
    示例 2:
    输入:n = 6, edges = [[1,2],[1,3],[2,4],[3,5],[3,6]]
    输出:6
    解释:恰好有一个质数编号的节点路径有:
  • (1, 2) 因为路径 1 到 2 只包含一个质数 2 。
  • (1, 3) 因为路径 1 到 3 只包含一个质数 3 。
  • (1, 4) 因为路径 1 到 4 只包含一个质数 2 。
  • (1, 6) 因为路径 1 到 6 只包含一个质数 3 。
  • (2, 4) 因为路径 2 到 4 只包含一个质数 2 。
  • (3, 6) 因为路径 3 到 6 只包含一个质数 3 。
    只有 6 条合法路径。
    提示:
    1 <= n <= 105
    edges.length == n - 1
    edges[i].length == 2
    1 <= ui, vi <= n
    输入保证 edges 形成一棵合法的树。

深度优先搜索

以任意节点为根,任意两个节点的路径一定是: 起点 → \rightarrow → 公共祖先 → \rightarrow → 终点,特例:起点或终点就是公共祖先。我们枚举公共祖先。DFS返回两个值:子树根节点为起点,子树的任意节点为终点的路径数。第一个返回值:经过一个质数。第二个返回值:经过0个质数。

分情况讨论

一,子树根节点是非质数。
a,特例。各子树经过1质数的路径。
b,各子树0质数的路径和兄长1质数路径结合。
c,各个子数1质数路径和兄长0质数路径结合。
二,子树根节点是质数。
a,特例。各子树经过0质数的路径和。
b,各子树0质数的路径和兄长0质数路径结合。
总结:
左支:a,根节点。 b,根节点+兄长节点的某一路径。
右支:当前支。
总共两种情况:
一:左支1质数,右支0 质数。
二:左支0质数,右支1 质数。

代码

核心代码

class CNeiBo2
{
public:
	CNeiBo2(int n, bool bDirect, int iBase = 0) :m_iN(n), m_bDirect(bDirect), m_iBase(iBase)
	{
		m_vNeiB.resize(n);
	}
	CNeiBo2(int n, vector<vector<int>>& edges, bool bDirect, int iBase = 0) :m_iN(n), m_bDirect(bDirect), m_iBase(iBase)
	{
		m_vNeiB.resize(n);
		for (const auto& v : edges)
		{
			m_vNeiB[v[0] - iBase].emplace_back(v[1] - iBase);
			if (!bDirect)
			{
				m_vNeiB[v[1] - iBase].emplace_back(v[0] - iBase);
			}
		}
	}
	inline void Add(int iNode1, int iNode2)
	{
		iNode1 -= m_iBase;
		iNode2 -= m_iBase;
		m_vNeiB[iNode1].emplace_back(iNode2);
		if (!m_bDirect)
		{
			m_vNeiB[iNode2].emplace_back(iNode1);
		}
	}
	const int m_iN;
	const bool m_bDirect;
	const int m_iBase;
	vector<vector<int>> m_vNeiB;
};

vector<int> CreatePrime(int iMax)
{
	vector<int> vPrime = { 2 };
	for (int i = 3; i <= iMax; i++)
	{
		bool b = true;
		for (const auto& n : vPrime)
		{
			if (0 == i % n)
			{
				b = false;
				break;
			}
		}
		if (b)
		{
			vPrime.emplace_back(i);
		}
	}
	return vPrime;
}

class Solution {
public:
	long long countPaths(int n, vector<vector<int>>& edges) {
		auto v = CreatePrime(n);
		n_setPrime.insert(v.begin(), v.end());
		CNeiBo2 neiBo2(n, edges, false, 1);
		for (auto& v : neiBo2.m_vNeiB)
		{
			sort(v.begin(), v.end());
		}
		DFS(neiBo2.m_vNeiB, 0, -1);
		return m_llRet;
	}
	pair<long long, long long> DFS(vector<vector<int>>& neiBo, int cur, int par)
	{
		long long i0=0, i1=0;
		if (n_setPrime.count(cur+1))
		{
			i1 = 1;			
		}
		else
		{
			i0 = 1;
		}		
		for (const auto& next : neiBo[cur])
		{
			if (next == par)
			{
				continue;
			}
			
			const auto [i01, i11] = DFS(neiBo, next, cur);
			m_llRet += i11 * i0;
			m_llRet += i01 * i1;
			if (n_setPrime.count(cur + 1))
			{					
				i1 += i01;				
			}
			else
			{
				i0 += i01;
				i1 += i11;
			}
		}
		
		return { i0,i1 };
	}
	unordered_set<int> n_setPrime;
	long long m_llRet = 0;
};
  • 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
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107

测试用例

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()
{		
	int n;
	vector<vector<int>> edges;
	{
		Solution sln;
		n = 5, edges = { {1,2},{1,3},{2,4},{2,5} };
		auto res = sln.countPaths(n, edges);
		Assert(res,4);
	}

	{
		Solution sln;
		n = 6, edges = { {1,2},{1,3},{2,4},{3,5},{3,6} };
		auto res = sln.countPaths(n, edges);
		Assert(res, 6);
	}
	
}

  • 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

2023年9月

class Solution {
public:
long long countPaths(int n, vector& edges) {
std::set setPrime = { 2,3 };
for (int i = 4 ; i <= n; i++)
{
bool bPrime = true;
for (const int j : setPrime)
{
if (j * j > i)
{
break;
}
if (0 == i % j)
{
bPrime = false;
break;
}
}
if (bPrime)
{
setPrime.emplace(i);
}
}
CUnionFind uf(n);
for (const auto& v : edges)
{
if (setPrime.count(v[0]) || setPrime.count(v[1]))
{//联通所有非素数
continue;
}
uf.Union(v[0] - 1, v[1] - 1);
}
vector vNodeNum = uf.GetNodeCountOfRegion();
std::unordered_map mPreRegion;
for (const auto& v : edges)
{//素数和那些联通区域联通
const int n0 = v[0] - 1;
const int n1 = v[1] - 1;
if (setPrime.count(v[0]) && (!setPrime.count(v[1])))
{
mPreRegion[v[0]].emplace(uf.GetConnectRegionIndex(n1));
}
if (setPrime.count(v[1]) && (!setPrime.count(v[0])))
{
mPreRegion[v[1]].emplace(uf.GetConnectRegionIndex(n0));
}
}

	long long llRegion1 = 0, llRegion2 = 0;
	for (const auto& [tmp, setRegion] : mPreRegion)
	{
		int llSumNode = 0;
		for (const auto& region : setRegion)
		{
			llRegion1 += vNodeNum[region];//起点一个素数一个非素数
			llSumNode += vNodeNum[region];
		}
		for (const auto& region : setRegion)
		{
			llRegion2 += vNodeNum[region] * (llSumNode-vNodeNum[region]);
		}
	}
	return llRegion1 + llRegion2/2;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

};

扩展阅读

视频课程

有效学习:明确的目标 及时的反馈 拉伸区(难度合适),可以先学简单的课程,请移步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/135995170"。版权归原作者所有,此博客不拥有其著作权,亦不承担相应法律责任。如有侵权,请联系我们删除。
复制链接
复制链接
相关推荐
发表评论
登录后才能发表评论和回复 注册

/ 登录

评论记录:

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

分类栏目

后端 (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