POJ 3349 - 数值哈希(这辈子只服数据量)

1.Question:

题意描述:
输入:n代表有多少片雪花
之后的n行,每行6个数字代表雪花的6个枝杈的长度(正序逆序都可以)

输出:
如果以上的雪花中出现了同构的情况,那么我们认为出现了相同的雪花

最后如果我们认为有两种相同的雪花,输出Twin snowflakes found.
否则输出No two snowflakes are alike.

2.Solution:

本题的数据量是我最想吐槽的,而不能提的思路是哈希(为了优化速度,不然铁定超时)
1.朴素思路:
如果我们采用数组直接存储的话,对于每一中的情况我们都需要进行往前的回溯判断,总共需要
T(n)=1+2+...+n=O(n^2)
的时间复杂度,本题来说的话10000^2是一定会超时的,在这里我们就要考虑优化了

2.优化思路1:
我们这里哈希的本质思路是对雪花进行分类,我们分类的依据是六个分叉的和相同的试做一个类,这样的话,我们对雪花进行分类之后可以通过(类似邻接表的思路)快速的减少遍历的次数来进行快速的正确的判断,如果数据哈希的均匀的话,可以无限接近于O(1)

3.TLE+RE的教训
首先我们来说说RE,在本题中因为我多次的TLE之后让我误以为是散列的范围不够大,所以我将哈希函数改成平方和对10w取模,但是在计算的过程之后发现TLE变成了RE,在这里我认为是因为本题的数据量有点大在100w的数据平方之后导致对于long型数据也溢出了,所以我们最好不要采用平方和的哈希方案

对于TLE有这么几点:
1.首先,我们的不要用线性探测的思路去做,因为线性探测是会出现二次聚集的情况的,我们最好的思路是采用拉链法构建哈希表
2.本题的用平方和的思路是可以做的,但是要求我们对结果要不断的取模控制溢出,但是这里也就引入了驱魔运算的效率的问题,我们都知道木偶运算是非常的低效的,索引说对于大数量的取模操作耗时也是让我们在本体的数据量下变得难以接受
3.最后,也是最重要的一点,我们千万不要为了节约内存对节点的保存数组用int*指针来代替,在用的时候我们动态分配内存的时间是非常的恐怖的,这也就是我TLE的根本所在

ps:本题判断雪花是不是相同的,我们采用了一点点优化,我摘引自别的大神,其他的判断方式当然也可以,但是这样写的非常的直观

3.Code:

/*
Problem: 3349		User: lantianheyeqi
Memory: 8908K		Time: 3485MS
Language: G++		Result: Accepted
*/
#include"iostream"
#include"cstdio"
#include"cstdlib"
#include"cstring"
#define N 100001

using namespace std;

typedef struct node
{
	int data[6];
	struct node* last;
}point;

int n;
point snow[N];
int dp[6];

void init()
{
	for(int i=0;i<N;i++) snow[i].last=NULL;
}

bool issame(int* white)
{
	for(int i=0;i<6;i++)    //这里为了解决同构的问题,我们必须要考虑顺时针和逆时针,在这里我们引入了如下的方法进行优化,值得学习 
	{
		if((dp[0]==white[i]&&
		   dp[1]==white[(i+1)%6]&&
		   dp[2]==white[(i+2)%6]&&
		   dp[3]==white[(i+3)%6]&&
		   dp[4]==white[(i+4)%6]&&
		   dp[5]==white[(i+5)%6])
		   ||
		   (dp[0]==white[i]&&
		    dp[1]==white[(i+5)%6]&&
			dp[2]==white[(i+4)%6]&&
			dp[3]==white[(i+3)%6]&&
			dp[4]==white[(i+2)%6]&&
			dp[5]==white[(i+1)%6]))
		return true;
	}
	return false;
}

bool find(int key)
{
	point* k=snow[key].last;
	while(k!=NULL)
	{
		if(issame(k->data)) return true;
		k=k->last;
	}
	return false;
}

void insert(int key)
{
	point* k=new point;
	for(int i=0;i<6;i++) k->data[i]=dp[i];
	k->last=snow[key].last;
	snow[key].last=k;
}

int main()
{
	bool ok=false;
	init();
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
	{
		int sum=0;
		for(int j=0;j<6;j++) scanf("%d",&dp[j]),sum+=dp[j];
		sum=sum%N;
		if(find(sum)) ok=true;
		else insert(sum);
	}
	if(ok) printf("Twin snowflakes found.\n");
	else printf("No two snowflakes are alike.\n");
	return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值