题目链接:点击打开链接
题意:
计算回旋图标的个数,即选中三颗星星,分别作为回旋图标的起点,拐点和终点,假设现在有三个
星星分别为i,j,k,如果d(a[i],a[j]) == d(a[j],a[k])则表示找到了一个回旋图标,其中d(x,y)表示这两个点的欧氏距离
为了给它很大的希望(i,j,k)和(k,j,i)被认为是两个不同的回旋图标
为了形象解释题意。灵魂画师已上线。
题解:先看一下最直观的解法。三层for循环去解决。第一层for循环去枚举第二个 i 星星 ,第二层for循环去枚举其他点 j 到 i 点的距离,第三层for循环去找 找有没点 k ,让did(i,j) == dis(i,k);看一眼数据范围1e4.n的三次方的时间复杂度绝对过不去。
那么考虑一下优化。思考一下绝对省不掉的步骤,就是计算任意两个点之间的距离。那么我们能做什么呢?
既然不能枚举3个点。那么我们换个思考方式,就是枚举回旋的图标的终点。也就是枚举中间点。若这个中间点与的其他点的距离有两个或者两个以上相等。说明这个点可以作为回旋图标的中间点。然后在考虑一个问题,如何去判断距离之间相等。for循环一个一个比较么?肯定不对啊。这样时间复杂度有上到n的三次方了。并且我们还有知道相等的距离有多少个。所以桶排序这种数据结构就用上了。这时候可能就有细心的同学可能会问。距离有可能成为double类型的数据么。数组没法记录啊。
想的很好。就是会出现这个问题。遇到问题就去解决问题。我们map一下不就解决了。
把浮点型的数字对应成一个int的数字。(场上想出来的骚操作=-=)。
最后统计出来那些数字相等之后。就变成总n个物品中取两个的问题。当然了结果要*2。因为对于同一个正过来是回旋图标,反过来也是回旋图标。
最后看代码实现:
#include <iostream>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <map>
using namespace std;
const int maxn = 1005;
struct node{
int x;
int y;
}Point[maxn];
double dis(node a,node b){
return sqrt((a.x - b.x)*(a.x - b.x) + (a.y - b.y) *(a.y - b.y));
}
int main(){
int z;
cin >> z;
while(z--){
int n ;
cin >> n;
for(int i = 0 ; i < n ; i ++){
scanf("%d%d",&Point[i].x,&Point[i].y);
}
if(n < 3){ // 小于3个点 绝对不会出现的
cout << "WA" << endl;
continue;
}
int ans = 0;
for(int i = 0 ; i < n ; i ++){
map<double,int> mp; // 骚操作
int num = 0; // 把每个double型的值 对应成int
int res[maxn];
memset(res,0,sizeof(res));
for(int j = 0 ; j < n ;j ++){
if (!mp.count(dis(Point[i],Point[j]))){ // 先判断这个数字是否出现过。没出现过 mp.count函数返回值为0。
mp[dis(Point[i],Point[j])] = num ++;
}
res[mp[dis(Point[i],Point[j])]]++; // 通桶排序
}
for(int i = 0 ; i < num ; i ++){
if(res[i] >= 2){
ans += res[i]*(res[i]-1)/2;
}
}
}
if(ans)
cout << ans * 2<< endl;
else
cout << "WA" << endl;
}
}