`
kenby
  • 浏览: 724681 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

poj 2002 二分查找,hash

 
阅读更多

算法过程:

1 将顶点按x坐标递增排序,若x相同,按y坐标递增排序,然后枚举所有边,对每一条由点p1和p2(根据排序p1 < p2)组成的边按照如下方式可唯一确定一个正方形:

    1) 将边绕p1逆时针旋转90度得到点p3

    2) 将边绕p2顺时针旋转90度得到点p4

则p1 p2 p3 p4组成一个正方形,设p1 = (x1,y1), p2 = (x2, y2),根据向量的旋转公式可以求出p3, p4的坐标为

p3 = (y1 - y2 + x1, x2 - x1 + y1)

p4 = (y1 - y2 + x2, x2 - x1 + y2)

 

2 然后搜索点p3和p4是否存在,若存在则找到一个正方形,计数加1,可以发现总是存在两条边确定的正方形是一样的,也就是说每个正方形会被发现2次,所以要将最后的计数结果除以2.

 

实现的时候关键是如何搜索某个点是否存在,由于所有点都排序过,所以可以用二分查找来搜索,但速度比较慢,至少1000ms, hash的速度更快些,可以达到几百ms,还有hash表如果用开放地址线性探测法解决冲突的话,很容易超时,

而用链地址法解决冲突效果要好很多。下面是二分搜索和hash两种方法的代码实现。

 

二分查找

 

#include <stdio.h>
#include <string.h>

//#define DEBUG

#ifdef DEBUG
#define debug(...) printf( __VA_ARGS__) 
#else
#define debug(...)
#endif

#define N 1000

struct Point {
	int x;
	int y;
};

struct Point point[N];

int n;	/* 点的个数 */

/* 由于点已经按照坐标排序过,所以采用二分查找
 * 搜索点(x,y)是否存在,存在返回1,否则返回0
 */
int bsearch(int x, int y)
{
	int		m, s, t;

	s = 0;
	t = n-1;
	while (s <= t) {
		m = (s+t)/2;
		if (point[m].x == x && point[m].y == y) return 1;
		if (point[m].x > x || (point[m].x == x && point[m].y > y)) {
			t = m-1;
		}
		else {
			s = m+1;
		}
	}
	return 0;
}

int main()
{
	int 	x, y, i, j, count;

	while (scanf("%d", &n), n) {
		count = 0;
		for (i = 0; i < n; i++) {
			scanf("%d %d", &x, &y);
			//插入法对点排序,按照x从小到大,y从小到大,且x优先排列的方式
			for (j = i-1; j >= 0; j--) {
				if (point[j].x > x || (point[j].x == x && point[j].y > y)) {
					point[j+1] = point[j];
				} else {
					break;
				}
			}
			point[j+1].x = x;
			point[j+1].y = y;
		}
		/* 枚举所有边,对每条边的两个顶点可以
		 * 确定一个唯一的正方形,并求出另外两个顶点的坐标
		 */
		for (i = 0; i < n; i++) {
			for (j = (i+1); j < n; j++) {
				//计算第三个点的坐标,搜索其是否存在
				x = point[i].y-point[j].y+point[i].x;
				y = point[j].x-point[i].x+point[i].y;
				if (bsearch(x,y) == 0) {
					continue;
				}
				//计算第四个点的坐标,搜索其是否存在
				x = point[i].y-point[j].y+point[j].x;
				y = point[j].x-point[i].x+point[j].y;
				if (bsearch(x, y)) {
					count++;
				}
			}
		}
		printf("%d\n", count/2);
	}
	return 0;
}
 

hash

 

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#define DEBUG

#ifdef DEBUG
#define debug(...) printf( __VA_ARGS__) 
#else
#define debug(...)
#endif

#define N 1000	/* 顶点个数 */
#define M 2999	/* hash表的大小,取素数冲突冲突较少 */

struct Point {
	int x;
	int y;
};

struct Point point[N];

/* 使用链地址法解决冲突, 表头不存数据 */
struct hash_entry {
	int x;
	int y;
	struct hash_entry *next;
};

struct hash_entry hash_table[M+1];

int conflict;

void insert(int x, int y) 
{
	unsigned int 	p;
	struct hash_entry *new_entry;

	p = (x*x+y*y)%M;	/* hash函数 */
	//创建一个新的entry
	new_entry = (struct hash_entry *)malloc(sizeof(struct hash_entry));
	new_entry->x = x;
	new_entry->y = y;
	/* 把新entry插在最前面,这样,越先插进来的entry在链表的越后面,
	 *最后一个entry的next指针为空
	 */
	new_entry->next = hash_table[p].next;
	hash_table[p].next = new_entry;
}


int find(int x, int y)
{
	unsigned int 	p;
	struct hash_entry *entry;

	p = (x*x+y*y)%M;	/* hash函数 */
	for(entry = hash_table[p].next; entry != 0 && (entry->x != x \
				|| entry->y != y); entry = entry->next, conflict++);	
	if (entry) {
		return 1;
	}
	return 0;
}

int main()
{
	int 	n, x, y, i, j, count;

	while (scanf("%d", &n), n) {
		memset(hash_table, 0, sizeof(hash_table));
		count = 0;
		for (i = 0; i < n; i++) {
			scanf("%d %d", &x, &y);
			//插入法对点排序,按照x从小到大,y从小到大,且x优先排列的方式
			for (j = i-1; j >= 0; j--) {
				if (point[j].x > x || (point[j].x == x && point[j].y > y)) {
					point[j+1] = point[j];
				} else {
					break;
				}
			}
			point[j+1].x = x;
			point[j+1].y = y;
			insert(x, y);
		}
		/* 枚举所有边,对每条边的两个顶点可以
		 * 确定一个唯一的正方形,并求出另外两个顶点的坐标
		 */
		for (i = 0; i < n; i++) {
			for (j = (i+1); j < n; j++) {
				//计算第三个点的坐标,搜索其是否存在
				x = point[i].y-point[j].y+point[i].x;
				y = point[j].x-point[i].x+point[i].y;
				if (!find(x, y)) {
					continue;
				}
				//计算第四个点的坐标,搜索其是否存在
				x = point[i].y-point[j].y+point[j].x;
				y = point[j].x-point[i].x+point[j].y;
				if (find(x, y)) {
					count++;
				}
			}
		}
		printf("%d\n", count/2);
	}
	debug("conflict = %d\n", conflict);
	return 0;
}
 

 

分享到:
评论

相关推荐

    经典 的POJ 分类

    - POJ 3373、POJ 1691:二分查找技术的应用。 ### 动态规划 #### 二维动态规划 - **题目示例**: - POJ 1191、POJ 1054:基于矩阵的状态转移方程。 - POJ 3280、POJ 2029:多维状态空间的探索。 #### 递归动态...

    poj推荐50题

    快速查找技术如二分查找、哈希等,在数据结构和算法中占有重要地位,能够大大提高程序效率。 #### 题目示例及解析: - **2503** 和 **2513** 两题适合了解快速查找的基本原理。 - **1035** 和 **1200** 这两个题目...

    Hash相关题解1

    商店价格排名问题可以通过哈希表来快速查找特定商店的价格变化,同时通过排序和二分查找来提高效率。直接使用STL中的map也可以,但可能会较慢。 5. POJ 3087【基础】 本题是关于木片堆叠的问题,可以模拟每次操作并...

    参加ACM大赛应该准备哪些课程? (2).pdf

    4. 二分查找 5. 叉乘、判线段相交、凸包 6. BFS、DFS、hash表 7. 数学上的辗转相除、线段交点、多角形面积公式 数据结构 1. 线段树 2. 并查集 3. 动态规划的各个典型(LCS、最长递增子串、三角剖分、记忆化dp) 4....

    北大ACM 题目分类

    - **题目**: 包括字符串处理(`hash`)、树状数组(`poj3253`)等。 - **所需知识**: 字符串处理技术、树状数组的应用以及Trie树等相关数据结构的构建与查询。 ### 二、图论 #### 1. 图的表示与基本操作 - **题目**: ...

    2018数据结构理论课.zip

    PPT可能还介绍了更高效的查找技术,如二分查找。 7. **23KMP和TRIE.ppt** - KMP算法是一种改进的字符串匹配算法,避免了不必要的回溯。而Trie(字典树或前缀树)是一种高效存储字符串集合的数据结构,特别适合于...

    PKU 的一些搜索题目

    根据题目编号,可以推测这是一个关于搜索的问题,可能是关于查找图中某个特定路径或最短路径的问题。 #### 总结 以上就是根据提供的文件信息整理出的一些关于北京大学编程比赛中搜索题目的知识点。这些题目涵盖了...

    常用计算机算法列表.pdf

    二分查找是一种高效的搜索方法,适用于有序数组,可以在对数时间内找到目标值。 叉乘用于计算向量的叉积,可用于判断线段是否相交。凸包算法如Graham扫描法,用于找到一组点的最小凸多边形包围。 BFS(广度优先...

    常用算法 (2).pdf

    4. **二分查找**:在有序数组中查找特定元素,时间复杂度为O(log n)。 5. **叉乘、线段相交与凸包**:在几何问题中常用,如计算点积判断线段是否垂直,以及计算凸包。 6. **BFS(广度优先搜索)与DFS(深度优先...

    ACM新手入门练习题

    - **1634**:二分查找的应用,如寻找最接近的目标值。 - **1689**:图论中的经典问题,如最小生成树的构建。 #### 四、综合实战案例 除了以上提到的基础和高级算法外,还有一些题目要求综合运用多种技术点来解决...

Global site tag (gtag.js) - Google Analytics