数学思维的魅力:用容斥原理与等差数列求和解决力扣倍数求和问题

数学思维的魅力:用容斥原理与等差数列求和解决力扣倍数求和问题_第1张图片

本篇博客会讲解力扣“2652. 倍数求和”的解题思路,这是题目链接。

老规矩,先来审题:
数学思维的魅力:用容斥原理与等差数列求和解决力扣倍数求和问题_第2张图片
以下是输出示例:
数学思维的魅力:用容斥原理与等差数列求和解决力扣倍数求和问题_第3张图片
以下是提示:
数学思维的魅力:用容斥原理与等差数列求和解决力扣倍数求和问题_第4张图片

思路1

你当然可以采用暴力枚举的方式,找到所有能被3、5、7整除的数,并求和,但这样时间复杂度是O(N),效率不够。

int sumOfMultiples(int n) {
	int sum = 0;
	// 找出能被3、5、7整除的数,并求和
	for (int i = 1; i <= n; ++i)
	{
		if (i % 3 == 0 || i % 5 == 0 || i % 7 == 0)
		{
			sum += i;
		}
	}

	return sum;
}

数学思维的魅力:用容斥原理与等差数列求和解决力扣倍数求和问题_第5张图片
这样是能够通过的,但这个思路不是今天的主角。

思路2

我们可以用数学的方法,推出时间复杂度为O(1)的思路,也就是用数学公式直接计算。

假设sum(n, m)表示在[1,n]中,所有能够被m整除的数之和。那么,我们要求的是多少呢?事实上,根据容斥原理,能够被3、5、7整除的数据之和等于sum(n, 3) + sum(n, 5) + sum(n, 7) - sum(n, 15) - sum(n, 21) - sum(n, 35) + sum(n, 105)

接下来的问题是,如何求sum(n, m)呢?在[1,n]中,所有能够被m整除的数构成等差数列,其中首项是m,公差是m,项数是c=n/m,那么就可以求出末项,即末项=首项+(项数-1)*公差,即m + (c-1)*m。接着就可以求出和为(首项+末项)*项数/2,即(m + m + (c-1)*m) * c / 2,化简得m * (c + 1) * c / 2

int sum(int n, int m)
{
	// 这些能被m整除的数构成等差数列
	// 首项:m
	// 公差:m
	// 项数:n/m
	int c = n / m;
	// (首项+末项)*项数/2
	// 末项=首项+(项数-1)*公差
	//return (m + m + (c-1)*m) * c / 2;
	return m * (c + 1) * c / 2;
}

int sumOfMultiples(int n) {
	// 容斥原理
	return sum(n, 3) + sum(n, 5) + sum(n, 7)
		- sum(n, 15) - sum(n, 21) - sum(n, 35)
		+ sum(n, 105);
}

数学思维的魅力:用容斥原理与等差数列求和解决力扣倍数求和问题_第6张图片
这样也能通过。

总结

善用数学的方法,把问题的时间复杂度降到O(1)。

感谢大家的阅读!

你可能感兴趣的:(力扣刷题,leetcode,算法,职场和发展,c语言,数学)