POJ2002

题意

给出n个点坐标,求能组成几个正方形。

思路

没想出思路...太久没打了越来越菜...

网上提供思路是:找出两个点,然后找出与这两个点对应组成正方形的对应俩点,判断这两个点是否存在。

思路出来后就是数学问题了,可分为已知相邻俩点和对顶点。

网上大多是相邻点的思路,相邻点组成的边会有两种情况,数学公式:

x3=x1+(y1-y2)  y3=y1-(x1-x2)
x4=x2+(y1-y2)  y4=y2-(x1-x2)
或
x3=x1-(y1-y2)  y3= y1+(x1-x2)
x4=x2-(y1-y2)  y4= y2+(x1-x2)

为什么可以看图:

自己想的思路是对顶点,好处是一条边只能组成一个正方形,不会存在两种情况,但是坏处必须用Double数据类型,数学公式:

x1=(a-b+c+d)/2,y1=(a+b-c+d)/2
x2=(a+b+c-d)/2,y2=(-a+b+c+d)/2

推理:

设正方形中bai,已知对角的两个顶点du的坐标分别是(a,b)、(c,d),未知的另两个顶点zhi的坐标是(x,y),则:
① (x-a)²+(y-b)²=(x-c)²+(y-d)²;
② (x-a)(x-c)+(y-b)(y-d)=0,

公式出来后,就比较简单了,但是暴力做法会导致超时,此处也有两种思路:

  • Hash寻址+拉链法
  • STL二分查找

代码

Hash寻址+拉链法:

//author: svtter
//

#include <iostream>
#include <stdio.h>
#include <string.h>
#include <vector>
#include <map>
#include <algorithm>
#include <queue>
#include <cmath>
 
#define INF 0xffffff
#define lln long long
 
#ifdef ONLINE_JUDGE
#define FOI(file) 0
#define FOW(file) 0
#else
#define FOI(file) freopen(file,"r",stdin);
#define FOW(file) freopen(file,"w",stdout);
#endif
 
using namespace std;
 
#define MAXN 1010 
#define MOD 1009
struct node
{
    int x, y;
    bool operator == (const node &a)const
    {
        return (x == a.x && y == a.y);
    }
    bool operator < (const node &a)const
    {
        if(x!=a.x)
            return x <a.x;
        return y < a.y;
    }
};
node point[MAXN];
int hash[MAXN+1], next[MAXN];
 
 
 
int n;
 
node c, d;
void getPoint(node &a, node &b) 
{
    c.x = b.x + (b.y - a.y);
    c.y = b.y - (b.x - a.x);
    d.x = a.x + (b.y - a.y);
    d.y = a.y - (b.x -a.x);
}
 
int getHash(node &a)
{
    return abs(a.x+ a.y) % MOD;
}
 
void insertHash(int i)
{
    //头插入,拉链法
    int key = getHash(point[i]);
    next[i] = hash[key];
    hash[key] = i;
}
 
bool search(node &t)
{
    int key = getHash(t);
    int i = hash[key];
    while(i != -1)
    {
        if(point[i] == t)
            return true;
        i = next[i];
    }
    return false;
}
 
int main()
{
    FOI("input");
    //FOW(output);
 
    int i, j;
    int ans;
    //write your programme here
    while(~scanf("%d", &n))
    {
        if(n == 0)
            break;
        memset(hash , -1 ,sizeof(hash));
        memset(next , -1, sizeof(next));
        for(i = 0; i < n; i++)
            scanf("%d%d", &point[i].x, &point[i].y);
        sort(point ,point+n);
        //puts("input over.");
 
        for(i = 0; i < n; i++)
            insertHash(i);
        //puts("build hash over.");
 
        ans = 0;
        for(i = 0; i < n; i++)
        {
            for(j = i+1; j < n; j++)
            {
                getPoint(point[i], point[j]);
                if(search(c) && search(d))
                    ans++;
            }
        }
 
        printf("%d\n", ans/2);
    }
 
    return 0;
}

转自:https://blog.csdn.net/svitter/article/details/38384871?utm_medium=distribute.pc_relevant.none-task-blog-title-2&spm=1001.2101.3001.4242

二分查找:

#include<iostream>
#include<cstdio> 
#include<algorithm>
using namespace std;
const int Max = 1005;

struct data{
    int x, y;
}node[Max];

bool cmp(const data &a, const data &b){   // 由于下面调用的binary_search(),故要用加const。 
    if(a.x == b.x) return a.y < b.y;
    return a.x < b.x;
}

int main(){
    int n, i, j;
   while(scanf("%d", &n) && n){
       for(i = 0; i < n; i ++)
           scanf("%d%d", &node[i].x, &node[i].y);
       sort(node, node + n, cmp);      

       //  binary_search()需要先排序。这里自己要注意:x相等的y问题不用二维的二分,直接cmp,要理解清楚,就一顺序。
       int ans = 0;
       for(i = 0; i < n; i ++)
           for(j = i + 1; j < n; j ++){
               data tmp;
               tmp.x = node[i].x + node[i].y - node[j].y;
               tmp.y = node[i].y - node[i].x + node[j].x;
               if(!binary_search(node, node + n, tmp, cmp))   // 学会STL的二分。
                   continue;
               tmp.x = node[j].x + node[i].y - node[j].y;
               tmp.y = node[j].y - node[i].x + node[j].x;
               if(!binary_search(node, node + n, tmp, cmp))	   //cmp和sort()一样写法
                   continue;
               ans ++;
           }
       printf("%d\n", ans/2);
    }
    return 0;
}

总体肯定是STL封装的二分查找更简单,但是Hash寻址+拉链法思路也是重要的。

需要注意这题的几个坑,如果没有进行sort()排序,则必须考虑邻边的时候必须考虑两个公式,不然可能出现一个正方形的两条平行边在用公式计算时都算出的是另外两个点,导致这个正方形没被计算。

下面我看了一偏比较复杂的hash寻址结合自己代码写的:

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

const int prime = 1999;
int ans, n;
typedef class {
public:
	double x, y;
}Node;

typedef class HashTable {
public:
	double x, y;
	HashTable* next;
	HashTable() {
		next = 0;
	}
}Hashtable;

Node node[1010];
Hashtable* hashs[prime];

void insert(int k) {
	int key = ((int)(pow(node[k].x, 2) + pow(node[k].y, 2)))%prime;
	if (!hashs[key]) {
		Hashtable* temp = new Hashtable;
		temp->x = node[k].x;
		temp->y = node[k].y;
		hashs[key] = temp;
	}
	else {
		HashTable* temp = hashs[key];
		while (temp->next)
			temp = temp->next;
		temp->next = new HashTable;
		temp->next->x = node[k].x;
		temp->next->y = node[k].y;
	}
}

bool find(Node nod) {
	int key = ((int)(pow(nod.x, 2) + pow(nod.y, 2)))%prime;
	if (!hashs[key]) {
		return false;
	}
	else {
		Hashtable* temp = hashs[key];
		while (temp) {
			if (temp->x == nod.x && temp->y == nod.y)
				return true;
			temp = temp->next;
		}
	}
	return false;
}

void deal(Node a, Node c) {
	Node mid_node;
	Node b, d;
	b.x = (a.x - a.y + c.x + c.y) / 2;
	b.y = (a.x + a.y - c.x + c.y) / 2;
	d.x = (a.x + a.y + c.x - c.y) / 2;
	d.y = (-a.x + a.y + c.x + c.y) / 2;
	if (find(b) && find(d)) ans++;
}

int main() {
	while (cin >> n && n) {
		ans = 0;
		memset(hashs, 0, sizeof(hashs));
		for (int i = 0; i < n; i++) {
			scanf("%lf %lf", &node[i].x, &node[i].y);
			insert(i);
		}
		for (int i = 0; i < n - 1; i++) {
			for (int j = i + 1; j < n; j++) {
				deal(node[i], node[j]);
			}
		}
		cout << ans/2 << endl;
	}
}
posted @ 2020-11-24 17:42  AkimotoAkira  阅读(63)  评论(0编辑  收藏  举报