三角形(triangle)

三角形(triangle) ⁡ \operatorname{三角形(triangle)} (triangle)

题目链接: SSL比赛 1143 ⁡ \operatorname{SSL比赛\ 1143} SSL 1143

题目

平面上有 n n n 个点,求出用这些点可以构成的三角形数。

输入

第一行一个整数 n n n

接下来 n n n 行,每行两个整数,表示点的坐标。

输出

输出仅一个整数,表示所求答案。

样例输入

5
0 0
1 1
1 -1
-1 -1
-1 1

样例输出

8

数据范围

对于 50 % 50\% 50% 的数据, n < = 300 n<=300 n<=300

对于 100 % 100\% 100% 的数据, n < = 3000 n<=3000 n<=3000 ,坐标的绝对值不超过 1 0 4 10^4 104 ,保证没有重合的点。

思路

这道题是一道数学题。

我们可以知道,三个点不能组合成三角形,当且仅当这三个点在一条直线上,就是其中一个点和另外两个点分别组成的直线的斜率是相等的。

啥?你不知道怎么求斜率?!
行吧。

就是我们可以知道直线的函数可以表示为 y = k x + b y=kx+b y=kx+b ,而斜率就是 k k k ,可以表示出这条直线有多斜,是怎样斜的。
那我们知道直线上的两个点怎么求斜率呢?
我们设两点坐标分别为 ( x 1 , y 1 ) (x_1,y_1) (x1,y1) ( x 2 , y 2 ) (x_2,y_2) (x2,y2)
那带进去前面的式子,就是这个:
{ y 1 = k x 1 + b y 2 = k x 2 + b \begin{cases}y_1=kx_1+b\\y_2=kx_2+b\end{cases} { y1=kx1+by2=kx2+b
然后我们就解二元一次方程,就可以得到:
{ k = y 1 − y 2 x 1 − x 2 b = y 1 − x 1 × y 1 − y 2 x 1 − x 2 \begin{cases}k=\dfrac{y_1-y_2}{x_1-x_2}\\b=y_1-x_1\times\dfrac{y_1-y_2}{x_1-x_2}\end{cases} k=x1x2y1y2b=y1x1×x1x2y1y2
那我们就得到了 k k k

我们可以先算出所有点中选三个点的排列有多少,然后再减去不能组合成的。
前面的排列很好求,如果点的数量是 n n n ,那就是 n × ( n − 1 ) × ( n − 2 ) / 2 / 3 n\times (n-1)\times(n - 2) / 2 / 3 n×(n1)×(n2)/2/3
但是不能组合成的种数怎么求呢?

我们可以枚举一个点,然后求出它和所有点组成的直线的斜率。然后排序,就可以看出斜率相同的种数和每一种有多少个的。
那我们可以知道,对于每一种斜率,设有 x x x 个这样的斜率,那答案就要减去 x × ( x − 1 ) / 2 x\times (x-1) / 2 x×(x1)/2
但是有一个特殊的东西,就是斜率不存在。
为什么会有这种情况呢,因为我们可以看到,如果这条直线是一条平行于 y y y 轴的直线,那 x 1 x_1 x1 就会等于 x 2 x_2 x2 ,就会使 x 1 − x 2 = 0 x_1-x_2=0 x1x2=0 ,造成除数是 0 0 0 的情况。
那我们就要特殊判断一下,单独拎出来处理即可。

代码

#include<cstdio>
#include<algorithm>
#define ll long long

using namespace std;

ll n, x[3001], y[3001], ans;
ll shu, num, nownum;
double xie[3001];

int main() {
    
    
	scanf("%lld", &n);//读入
	for (ll i = 1; i <= n; i++)
		scanf("%lld %lld", &x[i], &y[i]);
	
	ans = n * (n - 1) / 2 * (n - 2) / 3;//全部排列
	for (ll i = 1; i < n; i++) {
    
    
		shu = 0;
		num = 0;
		for (ll j = i + 1; j <= n; j++)
			if (x[i] == x[j]) shu++;//图像是竖着的(特殊处理)
				else xie[++num] = (double)(y[i] - y[j]) / (double)(x[i] - x[j]);//斜率
		
		sort(xie + 1, xie + num + 1);//排序
		
		ans -= shu * (shu - 1) / 2;//特殊处理竖着的这条这线上的点
		
		nownum = 1;
		for (ll j = 2; j <= num; j++)//找到相同的斜率的数量
			if (xie[j] == xie[j - 1]) nownum++;
				else {
    
    
					ans -= nownum * (nownum - 1) / 2;//处理这个斜率上的点
					nownum = 1;
				}
		ans -= nownum * (nownum - 1) / 2;
	}
	
	printf("%lld", ans);//输出
	
	return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_43346722/article/details/108551956