题目直角三角形

谁能够提出快一点的解题思路
我现在都超时

直角三角形

Time Limit:7000MS Memory Limit:65536K
Total Submit:81 Accepted:13

Description

给你N个座标平面上的点,请问总共可形成多少个直角三角形呢?

Input

输入档可能包含多笔测试资料。
每笔测试资料的第一列有一个正整数N,代表平面上的点数(3<=N<=1500)。当N=0的时候代表输入结束。
接下来有N列每列有一个座标,座标值都是介於 -10^9以及10^9之间的整数。
同一个座标上不会有两个点。

Output

请输出直角三角形的个数。

Sample Input

3
4 2
2 1
1 3
4
5 0
2 6
8 6
5 7
5
-1 1
-1 0
0 0
1 0
1 1
0

Sample Output

1
0
7
O(n^4)大概会过不了,因为n最大是1500

如果用斜率是可行,但遇到斜率无限大该如何?

怎么会有O(n^4)这么差劲的算法…… 最朴素的都是O(n^3), 取完所有3个点的组合方式, 判断下是否直角, 不就ok了?

较优算法应该是这样
1. 求所有两点组合的直线斜率, O(n^2)
2. 对上述斜率排序, 得到长度为n^2的斜率数组,O(n^2log(n^2))
3. 对于每个斜率k, 二分搜索-1/k这个斜率是否存在, 如果存在, 看是否可以构成三角形,O(n^2log(n^2))

就是这样了, 总的时间复杂度等于最大的一个 O(n^2log(n^2))
n是1500的话, 也就千万数量级, 肯定可以过

对于斜率无限大,可以特殊处理下, 因为 “座标值都是介於 -10^9以及10^9之间的整数” 所以最大斜率不会超过10^9. 你用20亿当作无限大就可以了。

修正一下, 上述算法可能会退化到n^4的情况, 以下算法应该不会
1. 遍历每个点, 分别假设每个点为直角三角形的顶点
2. 对于每个1中的顶点,找出所有经过它的直线(n-1条),并按斜率排序
3. 对于每条直线line, 二分搜索这n-1条直线, 找出与它垂直的直线linex,并且往前往后寻找与linex斜率相同的直线。

表述可能不大清楚 附ac的程序

#include <iostream>
#include <algorithm>
using namespace std;

const int MAXN = 1503, INF = 2000000000;
int n, x[MAXN], y[MAXN], linen;
struct Line { int x; double k; };
Line line[MAXN];

bool cmp(const Line &a, const Line &b)
{
return a.k < b.k;
}

bool in()
{
scanf("%d", &n);
if (!n) return false;
for (int i=0; i<n; i++)
scanf("%d%d", &x[i], &y[i]);
linen = 0;

return true;
}

int bsearch(int l, int r, double v, int p1, int p)
{
while (l <= r)
{
int mid = (l+r)/2;
if ((x[p1]-x[p])*(x[line[mid].x]-x[p])+(y[p1]-y[p])*(y[line[mid].x]-y[p]) == 0) return mid;
else if (line[mid].k > v) r = mid-1;
else l = mid + 1;
}
return -1;
}

int main()
{
while (in())
{
int ans = 0, t, t1, t2;
for (int i=0; i<n; i++)
{
linen = 0;
for (int j=0; j<n; j++)
{
if (j == i) continue;
line[linen].x = j, line[linen++].k = (x[i]==x[j]) ? INF : ((double)(y[i]-y[j])) / (x[i]-x[j]);
}
sort(line, line + linen, cmp);
for (int j=0; j<linen; j++)
{
if ((t=bsearch(j+1, linen-1, line[j].k==0 ? INF : -1/line[j].k, line[j].x, i)) == -1) continue;
ans++;
t1 = t2 = t;
while (--t1>j && line[t1].k == line[t].k) ans++;
while (++t2<linen && line[t2].k == line[t].k) ans++;
}
}
printf("%d\n", ans);
}
return 0;
}
温馨提示:答案为网友推荐,仅供参考
第1个回答  2009-10-10
我老眼昏花 没看见N有范围... 大概知道这题是什么方向的了

----

我估计我没办法再改回答了,不知道刚才天杀的屏蔽算不算修改次数,发我消息吧TvT
第2个回答  2009-10-10
我有一个算法,不过程序的效率比较低,估计是O(n^4)的复杂度,
大概的想法是:
计算任两点之间的距离,(这部分的复杂度约为n*n)
对距离进行排序,(这部分的复杂度要根据选用的算法来判断,二分法应该好些)
在根据勾股定理判断是否存在直角三角形。也就是依次检查是否存在两边的平方和等于第三边,之所以前面要排序,就是为了避免重复。
大致的思路就是这样了,不知还有没有更有效率的方法。
第3个回答  2009-10-13
可以利用相互垂直直线的斜率互为负倒数来做
第4个回答  2009-10-13
我觉得以上都忽略了一个问题,垂直不等于能形成三角形。
我的想法是找直角的顶点,方法很简单,复杂度估计一般,这个我不懂。
假设某一个点是直角顶点,然后再分别取其他两个点,根据斜率是负倒数的原理,即(x1-x2)*(x1-x3)+(y1-y2)*(y1-y3)=0来判断是否是直角。其中(x1,y1)是可能为直角顶点的点。
复杂度的话应该是n*(n-1)(n-2)吧。
第5个回答  2009-10-17
任取3点,组成两个矢量,如果矢量叉乘积等于0,则两矢量相互垂直,可构成直角三角形。
3点组成两个矢量,有3个情况,矢量叉乘积等于0:
(x2-x1)*(y3-y1) + (x3-x1)*(y2-y1) == 0
(x3-x2)*(y1-y2) + (x1-x2)*(y3-y2) == 0
(x1-x3)*(y2-y3) + (x2-x3)*(y1-y3) == 0
满足上3式中任一式,就是直角三角形。

n 个点,取3个,它的个数 用 “组合” n 取3 计算