闻缺陷则喜何志丹

闻缺陷则喜何志丹

涉及知识点

最大公约 调和级数 并集查找(并差集)
质数、最大公约数、菲蜀定理

LeetCode 2709. 最大公约数遍历

给你一个下标从 0 开始的整数数组 nums ,你可以在一些下标之间遍历。对于两个下标 i 和 j(i != j),当且仅当 gcd(nums[i], nums[j]) > 1 时,我们可以在两个下标之间通行,其中 gcd 是两个数的 最大公约数 。
你需要判断 nums 数组中 任意 两个满足 i < j 的下标 i 和 j ,是否存在若干次通行可以从 i 遍历到 j 。
如果任意满足条件的下标对都可以遍历,那么返回 true ,否则返回 false 。
示例 1:
输入:nums = [2,3,6]
输出:true
解释:这个例子中,总共有 3 个下标对:(0, 1) ,(0, 2) 和 (1, 2) 。
从下标 0 到下标 1 ,我们可以遍历 0 -> 2 -> 1 ,我们可以从下标 0 到 2 是因为 gcd(nums[0], nums[2]) = gcd(2, 6) = 2 > 1 ,从下标 2 到 1 是因为 gcd(nums[2], nums[1]) = gcd(6, 3) = 3 > 1 。
从下标 0 到下标 2 ,我们可以直接遍历,因为 gcd(nums[0], nums[2]) = gcd(2, 6) = 2 > 1 。同理,我们也可以从下标 1 到 2 因为 gcd(nums[1], nums[2]) = gcd(3, 6) = 3 > 1 。
示例 2:
输入:nums = [3,9,5]
输出:false
解释:我们没法从下标 0 到 2 ,所以返回 false 。
示例 3:
输入:nums = [4,3,12,8]
输出:true
解释:总共有 6 个下标对:(0, 1) ,(0, 2) ,(0, 3) ,(1, 2) ,(1, 3) 和 (2, 3) 。所有下标对之间都存在可行的遍历,所以返回 true 。

提示:
1 <= nums.length <= 10
1 <= nums[i] <= 10

并集查找

每个数的下标都看成一个节点,最大公约数大于1,则相连。本题    ⟺    \iff 只有一个连通区域。
枚举x ∈ \in [2,m] ,如果nums[i]是x的倍数,则和前一个x的倍数相连。时间复杂度:O(nlogn),就是n × \times ×调和级数之和。

一,将相同的值x连接起来,x>1。
二,将x的倍数连起来。
三,判断是否只有一个连通区域。

代码

核心代码

class CUnionFind
{
public:
	CUnionFind(int iSize) :m_vNodeToRegion(iSize)
	{
		for (int i = 0; i < iSize; i++)
		{
			m_vNodeToRegion[i] = i;
		}
		m_iConnetRegionCount = iSize;
	}	
	CUnionFind(vector<vector<int>>& vNeiBo):CUnionFind(vNeiBo.size())
	{
		for (int i = 0; i < vNeiBo.size(); i++) {
			for (const auto& n : vNeiBo[i]) {
				Union(i, n);
			}
		}
	}
	int GetConnectRegionIndex(int iNode)
	{
		int& iConnectNO = m_vNodeToRegion[iNode];
		if (iNode == iConnectNO)
		{
			return iNode;
		}
		return iConnectNO = GetConnectRegionIndex(iConnectNO);
	}
	void Union(int iNode1, int iNode2)
	{
		const int iConnectNO1 = GetConnectRegionIndex(iNode1);
		const int iConnectNO2 = GetConnectRegionIndex(iNode2);
		if (iConnectNO1 == iConnectNO2)
		{
			return;
		}
		m_iConnetRegionCount--;
		if (iConnectNO1 > iConnectNO2)
		{
			UnionConnect(iConnectNO1, iConnectNO2);
		}
		else
		{
			UnionConnect(iConnectNO2, iConnectNO1);
		}
	}

	bool IsConnect(int iNode1, int iNode2)
	{
		return GetConnectRegionIndex(iNode1) == GetConnectRegionIndex(iNode2);
	}
	int GetConnetRegionCount()const
	{
		return m_iConnetRegionCount;
	}
	vector<int> GetNodeCountOfRegion()//各联通区域的节点数量
	{
		const int iNodeSize = m_vNodeToRegion.size();
		vector<int> vRet(iNodeSize);
		for (int i = 0; i < iNodeSize; i++)
		{
			vRet[GetConnectRegionIndex(i)]++;
		}
		return vRet;
	}
	std::unordered_map<int, vector<int>> GetNodeOfRegion()
	{
		std::unordered_map<int, vector<int>> ret;
		const int iNodeSize = m_vNodeToRegion.size();
		for (int i = 0; i < iNodeSize; i++)
		{
			ret[GetConnectRegionIndex(i)].emplace_back(i);
		}
		return ret;
	}
private:
	void UnionConnect(int iFrom, int iTo)
	{
		m_vNodeToRegion[iFrom] = iTo;
	}
	vector<int> m_vNodeToRegion;//各点所在联通区域的索引,本联通区域任意一点的索引,为了增加可理解性,用最小索引
	int m_iConnetRegionCount;
};

class Solution {
public:
	bool canTraverseAllPairs(vector<int>& nums) {
		m_c = nums.size();
		const int iMax = *std::max_element(nums.begin(), nums.end());
		vector<vector<int>> vIndexs(iMax + 1);
		CUnionFind uf(m_c);
		for (int i = 0; i <m_c; i++) {
			auto& v = vIndexs[nums[i]];
			if (v.size()&&(1 != nums[i])) { uf.Union(v.back(), i); }
			v.emplace_back(i);
		}
		for (int x = 2; x <= iMax; x++) {
			int iPre = -1;
			for (int y = x; y <= iMax; y += x) {
				if (vIndexs[y].empty()) { continue; }
				if (-1 != iPre) { uf.Union(iPre, vIndexs[y].back()); }
				iPre = vIndexs[y].back();
			}
		}
		return uf.GetConnetRegionCount() == 1;
	}
	int m_c;
};

测试用例

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]);
	}
}

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

int main()
{
	vector<int> nums;
	{
		Solution slu;
		nums = { 1,1 };
		auto res = slu.canTraverseAllPairs(nums);
		Assert(false, res);
	}
	{
		Solution slu;
		nums = { 2, 3, 6 };
		auto res = slu.canTraverseAllPairs(nums);
		Assert(true, res);
	}
	{
		Solution slu;
		nums = { 3,9,5 };
		auto res = slu.canTraverseAllPairs(nums);
		Assert(false, res);
	}
	{
		Solution slu;
		nums = { 4,3,12,8 };
		auto res = slu.canTraverseAllPairs(nums);
		Assert(true, res);
	}
}

【最大公约 调和级数 并集查找】2709. 最大公约数遍历-LMLPHP

扩展阅读

视频课程

有效学习:明确的目标 及时的反馈 拉伸区(难度合适),可以先学简单的课程,请移步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++**实现。

【最大公约 调和级数 并集查找】2709. 最大公约数遍历-LMLPHP

05-09 18:48