回旋镖的数量
给定平面上 n 对不同的点,“回旋镖” 是由点表示的元组 (i, j, k) ,其中 i 和 j 之间的距离和 i 和 k 之间的距离相等(需要考虑元组的顺序)。
找到所有回旋镖的数量。你可以假设 n 最大为 500,所有点的坐标在闭区间 [-10000, 10000] 中。
示例:
输入:
[[0,0],[1,0],[2,0]]
输出:
2
解释:
两个回旋镖为 [[1,0],[0,0],[2,0]] 和 [[1,0],[2,0],[0,0]]
分析
1.对于一个点我们设为i,倘若另外两个点j、k,他们到i的距离都相同,那就构成一对回旋镖
2.注意:需要考虑元组的顺序,即可能j、k是相同的点,但因为在points中不是同一个元素,所以是两个点
3.且倘若j、k在坐标轴上是同一个点,那么(i,j,k)和(i,k,j)都可以构成回旋镖
那么如何求取距离呢?公式如下:
必然我们是要固定一个点i,然后求剩余所有点到i的距离,然后看是否构成回旋镖
但是题目没有要求是要求出哪些点,而只是单纯的求出可以构成回旋镖的个数就好了,所以我们也不必那么麻烦,单纯统计距离的平方出现的次数就好了
问题来了:如何通过距离出现的次数来统计可构成回旋镖的的可能组合有多少个呢?
1.因为统计的是距离的平方出现的次数,必然在该次数中,所有点到原点i的距离都是一样的。
2.其实就是在一堆集合中,挑取任意的两个点作为j、k来构成回旋镖。
例子:对于每一个点,都计算其它点与它的距离,然后把这个距离放入map中作为key,满足这一距离的其它点的个数作为value。
比如与点A相距10的点有5个,那么可能的组合就有5X4种方式。
遍历一遍关于点A的map,对于所有的距离下的个数都计算出可能的组合方式再相加,就得到对于一个点来说满足要求的所有组合。
在外面再套一层循环,就可以获得所有点相对于其它点的map,也就可以获得所有的组合个数,也就是我们需要的返回值
3.可能会有点小懵,但是下面这个公式就能很好说明了
没错就是最基本的排列组合公式,那么对于本题,公式中n=出现次数,m=2因为我们只用取两个点作为j、k动手化简一下
方法总结
1.固定一个点i,求所有点到i的距离的平方的出现次数
2.节省内存就一直用一个哈希表来存出现次数了
3.对于点i,求完其他的点到i的距离后,求由多少种可能回旋镖,即固定点i,j和k的可能性有多少种
遍历下一个点,直到结束
class Solution {
public:
int numberOfBoomerangs(vector<vector<int>>& points) {
int res=0;
int n=points.size();
for(int i=0;i<n;i++)
{
map<int,int>num;//对于每一个i点需要重新开辟一个哈希表用来存储到该点的距离和次数
for(int j=0;j<n;j++)
{
if(j!=i)
{
int distance=dis(points[i],points[j]);
num[distance]++;
}
}
for(map<int,int>::iterator it=num.begin();it!=num.end();it++)//每次对于一个点求取出所有到该点的距离和次数后,记性排列组合并叠加,例如到A点的距离有5和4,距离为5的点有3个,距离为4的点有2个那么排列组合res=3X2+2X1=8,这就是res要累加的原因。当A点结束后遍历下一个点,依次进行同样操作,直到结束
{
if(it->second>=2)
res+=(it->second)*(it->second-1);
}
}
return res;
}
private:
int dis(vector<int>&a,vector<int>&b)//两点之间的距离函数
{
return (a[0]-b[0])*(a[0]-b[0])+(a[1]-b[1])*(a[1]-b[1]);
}
};