fzu 2231 平行四边形数

题意:给出n个点,问哪四个点可以组成平行四边形


思路;要判别一个四边形是否是平行四边形,一个等价的判断条件是 两条边平行且相等,这样就是平行四边形,并且平行具有传递性,如果a//b,b//c,那么a//c,那么设g[i]为第i条边已经能组成平行四边形的个数,那么如果j//i且d[j]==d[i],g[j]=g[i]+1,可以在o(n)下处理,但要提前排好序,有点像dp的重复子问题,细节看代码


链接:http://acm.fzu.edu.cn/problem.php?pid=2231


#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
#include <cmath>
using namespace std;

const int inf = 0x3f3f3f3f;
const double eps = 1e-8;
int x[505], y[505];

typedef long long ll;

struct node
{
    int x, y;
}a[505];//存所有的点

struct edge
{
    int x1,y1,x2, y2,k2,k1;
    ll d;
    double k,b;
} b[300000];//存的是所有的边 ,最多有500*499条边

int g[300000];

ll cal(int x1, int y1, int x2, int y2)//边长
{
    return (ll)abs(x1-x2)*abs(x1-x2) + (ll)abs(y1-y2)*abs(y1-y2);
}

int gcd(int a,int b)
{
    return b==0?a:gcd(b, a%b);
}

void calk(int x1, int y1, int x2, int y2,int &x, int &y)//计算该边斜率
{
    x = x1-x2;
    y = y1 - y2;
    int d = gcd(x, y);
    x/=d, y/=d;
}

bool cmp(const edge &a, const edge &b)//先按距离排序,再按斜率排序
{
    if(a.d!=b.d)
        return a.d<b.d;
    else
    {
        return a.k<b.k;
    }
}

int main()
{
    int n;
    while(~scanf("%d", &n))
    {
        for(int i=0; i<n; i++)
        {
            scanf("%d%d", &a[i].x, &a[i].y);
        }
        int cnt = 0;
        for(int i=0; i<n; i++)
        {
            for(int j=i+1; j<n; j++)
            {
                b[cnt].x1 = a[i].x,b[cnt].y1 = a[i].y;
                b[cnt].x2 = a[j].x,b[cnt].y2 = a[j].y;
                b[cnt].d =cal(a[i].x, a[i].y,a[j].x,a[j].y;
                if(b[cnt].x1==b[cnt].x2)
                    b[cnt].k = inf;
                else
                {
                    calk(a[i].x, a[i].y,a[j].x,a[j].y, b[cnt].k2,b[cnt].k1);
                    b[cnt].k = (double)b[cnt].k1/b[cnt].k2;
                    b[cnt].b = (double)b[cnt].y1 - b[cnt].k*b[cnt].x1;
                }
                cnt++;//y = kx+b;
            }
        }
        int ans =0;
        memset(g, 0, sizeof(g));
        sort(b, b+cnt, cmp);
        for(int i= 1; i<cnt; i++)
        {
            if(b[i].d==b[i-1].d&&(fabs(b[i].k-b[i-1].k)<eps))//两条边边长相等,斜率相等
            {
                if(b[i].x1==b[i].x2)//平行y轴,则斜率为无限大
                {
                    if(b[i].x1!=b[i-1].x1) 
                    {
                        g[i]=g[i-1]+1;
                        ans += g[i];
                    }
                }
                else if(fabs(b[i].k-0)<eps) //平行x轴, 则斜率为0
                {
                    if(b[i].y1!=b[i-1].y1)
                    {
                        g[i]=g[i-1]+1;
                        ans += g[i];
                    }
                }
                else
                {
                    if(fabs(b[i].b-b[i-1].b)>eps)//两直线必须保证b不相等,
                    {
                        g[i]=g[i-1]+1;
                        ans += g[i];
                    }
                }
            }

        }
        printf("%d\n", ans/2);//因为一个平行四边有两组边平行且相等,所以重复算了两次
    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值