题意:
有N个点,每个点都有各自的坐标
先从N个点中取出三个点构成一个三角形,然后再从剩下的N-3个点中取出三个点构成一个三角形
求有多少种选择使得两个三角形可以经过旋转平移后重合
三角形 A, B - B, A 算是两种选择,但相同的三个点组成的三角形只能算一个三角形
首先可以确定,两个三角形一定全等,其次要对它是否能旋转得到进行判断
设三角形的三个顶点为A,B,C
当三角形旋转后,AB,BC,CA 的相对顺序是不变的
所以我们可以利用边的相对顺序来判断是否能够旋转得到
然后就是怎么得到边的相对顺序
先确定一个顺时针或逆时针方向作为标准,然后讨论,我确定的是逆时针方向
因为向量具有方向性,所以我们利用向量来确定方向
如果向量 BC 在向量 AB 的左边,则 A -> B -> C -> A 就是一个逆时针序
在右边,则 A -> C -> B -> A 为逆时针序
所以我们只要判断向量BC在向量AB的左侧还是右侧就可以得到边的逆时针序或顺时针序
设向量 AB 顺时针旋转到向量 BC 方向需要旋转的角度为 p
若 p < 180º 则BC在AB左侧 三条边顺序为 AB, BC, CA
若 p = 180º 则ABC三点一线,无法构成三角形
若 p > 180º 则BC在AB右侧 三条边顺序为 AC, CB, BA
角度p可以利用向量叉积来求
AB × BC = AB ∙ BC ∙ sin(p)
|AB × BC| = |AB| ∙ |BC| ∙ sin(p)
设 AB = (x1, y1, 0), BC = (x2, y2, 0)
AB × BC = (0, 0, x1*y2 - x2*y1)
|AB × BC| = x1*y2 - x2*y1
如果 x1*y2 - x2*y1 > 0 则 sin(p) > 0 => 0º< p <180º
x1*y2 - x2*y1 < 0 则 sin(p) < 0 => 180º< p <360º
也可以分类讨论,然后来判断每种状况,最后得到的所有情况化简合并后与使用叉积的结果相同
#include <iostream> #include <string.h> #include <stdio.h> #include <algorithm> #include <math.h> using namespace std; int T,n,cas = 0,ans; int x[15],y[15],vis[15]; int len(int a,int b){ return (x[a]-x[b])*(x[a]-x[b]) + (y[a]-y[b])*(y[a]-y[b]); } bool change(int &a,int &b,int &c){ // 得到边的顺序 int x1 = x[b] - x[a]; // AB 向量坐标 int y1 = y[b] - y[a]; int x2 = x[c] - x[b]; // BC 向量坐标 int y2 = y[c] - y[b]; int l1, l2, l3; l1 = l2 = l3 = 0; int tem = y2*x1 - x2*y1; if(tem > 0) { // a -> b -> c l1 = len(a, b); l2 = len(b, c); l3 = len(c, a); }else if(tem < 0) { // a -> c -> b l1 = len(a, c); l2 = len(c, b); l3 = len(b, a); }else return false; a = l1; b = l2; c = l3; return true; } void slove(int a,int b,int c){ int l1 = a, l2 = b, l3 = c; if(!change(l1, l2, l3)) return; for(int i=a+1;i<=n-2;i++){ if(vis[i]) continue; for(int j=i+1;j<=n-1;j++){ if(vis[j]) continue; for(int k=j+1;k<=n;k++){ if(vis[k]) continue; int L1 = i, L2 = j, L3 = k; if(!change(L1, L2, L3)) continue; if(l1==L1 && l2==L2 && l3==L3) ans ++; else if(l1==L2 && l2==L3 && l3==L1) ans ++; else if(l1==L3 && l2==L1 && l3==L2) ans ++; } } } } int main() { scanf("%d",&T); while(T--){ scanf("%d",&n); for(int i=1;i<=n;i++){ scanf("%d%d",&x[i],&y[i]); } ans = 0; for(int i=1;i<=n-2;i++){ for(int j=i+1;j<=n-1;j++){ for(int k=j+1;k<=n;k++){ vis[i] = vis[j] = vis[k] = 1; slove(i, j, k); vis[i] = vis[j] = vis[k] = 0; } } } printf("Case %d: %d\n",++cas, ans*2); } return 0; }