FZU - 2270 - Two Triangles (向量叉积|分类讨论)

FZU - 2270 题目链接

题意:

有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;
}


猜你喜欢

转载自blog.csdn.net/w326159487/article/details/80283227
two