给定 n×m 网格,共有多少三角形?

题目描述

给定一个nxm的网格,请计算三点都在格点上的三角形共有多少个。注意三角形的三点不能共线。

输入
输入两个正整数n和m,分别表示行数和列数。

输出
输出一个正整数,为所求三角形数量。

示例
输入:2 2
输出:76


解题思路

直接计算三角形个数(不共线三个点的所有可能性)有一定难度,所以可以反向思考,通过计算任取三个点的所有可能性,再减去所有共线的情况,结果就是不共线的所有情况,即所求三角形数量。

任取三个点

这里是排列组合中,组合 的基本定义:

从n个不同元素中,任取m(m≤n)个元素并成一组,叫做从n个不同元素中取出m个元素的一个组合;从n个不同元素中取出m(m≤n)个元素的所有组合的个数,叫做从n个不同元素中取出m个元素的组合数。用符号 C(n,m) 表示。(源自百度百科)
公式

对于 n×m 个网格,其网格点有 (n+1) × (m+1) 个。任取三个点的组合有 C((n+1) × (m+1), 3) 个。

共线

共线的情况可以分为两大类:

  1. 在同一行共线 / 在同一列共线
  2. 在对角线上共线
同一行上共线

每一行有(m+1)个网格点,在(m+1)个网格点任取三个点,即 C((m+1), 3)
一共有(n+1)行,故再乘以行数 C((m+1), 3) × (n+1)

同一列上共线

每一列有(n+1)个网格点,在(n+1)个网格点任取三个点,即 C((n+1), 3)
一共有(m+1)列,故再乘以列数 C((n+1), 3) × (m+1)

对角线上共线

首先需要了解gcd(最大公约数)算法:GCD算法(最大公约数算法)解析

假设线的一个端点固定在整个网格的左上角,即该端点坐标为(1,1),枚举其他所有的点(除去同一行/列上共线的点);
端点(1,1)与任意一点(x,y)((x,y)与(1,1)不在同一行/列上)的连线之间的点的数量为:
(gcd(i,j)-1) (1<=i<=n,1<=j<=m,i=x-1,j=y-1)

即有 (gcd(i,j)-1) 种三点共线的可能性。(请各位自己证明…)

以上结果,是基于端点在(1,1)这种情况。我们可以将 三点共线的线段 在网格内进行 平移 / 对称翻转,得到端点在其他位置的所有情况。

平移
平移得到的线段个数 = 水平移动的最大距离 × 垂直移动的最大距离,
(m+1-j) × (n+1-i)

对称翻转
这个很容易理解,只有正/反 2种 情况。


代码实现

JavaScript

function cnt(x){
	if( x<3 ) return 0
    return x * (x-1) * (x-2) / 6
}

function gcd(a,b){
	if(b == 0) return a
    else return gcd(b,a % b)
}

function triangle(n,m){     // n为行数,m为列数
	var sum = cnt((n+1)*(m+1))
    var row = (n+1) * cnt(m+1)
    var col = (m+1) * cnt(n+1)

     for(var i=1; i<=n; i++){
     	for(var j=1; j<=m; j++){
     		var temp = (gcd(i,j)-1) * (m+1-j) * (n+1-i) * 2
     	}
     }
	return sum - row - col - temp
}

console.log(triangle(2,2)) // 76
  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值