poj3349 Snowflake Snow Snowflakes 最小表示法 + 哈希

本文介绍了一种用于比对雪花序列的算法,旨在找出两片相同的雪花。通过将雪花序列转换为两种不同的最小表示形式并利用哈希表进行查重,有效解决了旋转和起始数字变化带来的比对难题。

题目网址:http://poj.org/problem?id=3349

题目大意:找两片相同的雪花,这里相同的定义是雪花的序列可以顺时针也可以逆时针,并且可以是任何一个数字开头,例如: 5 4 3 2 1 6  和  1 6 5 4 3 2 以及 3 4 5 6 1 2 是同一片雪花 

如果有相同的则输出:Twin snowflakes found.

否则输出:No two snowflakes are alike.

思路:不考虑顺时针和逆时针的话,同一片雪花的最小表示应该是一样的,如:1 2 3 4 5 6 和 2 3 4 5 6 1 的最小表示都是1 2 3 4 5 6 但是可以顺时针或者逆时针  比如将 1 2 3 4 5 6 反过来是 6 5 4 3 2 1按题意这两个是同一片雪花  但是 6 5 4 3 2 1 的最小表示是  1 6 5 4 3 2 1 是不同的 

综上 一片雪花存两个最小表示 在查重就行了

代码如下:

#include<cstdio>
#include<map>
#include<algorithm>
#include<cstring>
using namespace std;
const int maxn = 1e5+7;
const int seed = 1331;
typedef unsigned long long ull;
const int Hash = 3e5+7;
int n,x[15];
ull p;
int get_min(int *s){//最小表示法的模板
	int t = 6;
	for(int i = 1; i <= 6; i++)
	s[i+t] = s[i];
	int i = 1,j = 2,k;
	while(i <= t&&j<= t){
		for(k = 0; k < t && s[i+k] == s[j+k]; k++);
		if(k == t)break;
		if(s[i+k]>s[j+k]){
			i+=k+1;
			i+=(i==j);
		}
		if(s[j+k]>s[i+k]){
			j+=k+1;
			j+=(i==j);
		}
	}return min(i,j);//返回的是最小表示的第一个数的下标	
}
ull get_hash(int *s){//计算最小表示的哈希值
	int index = get_min(s);
	ull ret = 0;
	for(int i = index; i <= index+6-1; i++)
	ret = ret*seed + s[i];
	return ret;
}

struct hashmap{//用图来查重 本来用的map 果然超时间了
	int head[Hash],nex[maxn<<2],size;
	ull state[maxn<<2];//注意这里是maxn的两倍
	void init(){
		size = 0;
		memset(head,-1,sizeof(head));
	}
	bool insert(ull val){
		int h = val%Hash;
		for(int i = head[h]; i!=-1; i = nex[i])
			if(val == state[i])
			return true;
		state[size] = val;
		nex[size] = head[h];//一开始-1,邻接表往前插点
		head[h] = size++;
		return false; 
	}
} h;

int main(){
	int flag = 0;
	scanf("%d",&n);
	h.init();
	while(n--){
		for(int i = 1; i <= 6; i++)
		scanf("%d",&x[i]);
		p = get_hash(x);
		if(!flag)flag = h.insert(p);  
		reverse(x+1,x+1+6);
		p = get_hash(x);
		if(!flag)flag = h.insert(p); 
	}
	if(flag)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、付费专栏及课程。

余额充值