哈希做题笔记(持续更新)

雪花雪花雪花

雪花雪花雪花

思路:雪花六个角,通过旋转和翻转后能不能匹配到相同的两种排列,如果有,就代表两者相同,否则不同。其实可以简化为将两种排列进行旋转和翻转后得到两种最小的字典序排列,如果两者最小字典序排列相同,就证明两个相同。翻转可以分为奇数和偶数次奇数次相当于翻转一次,偶数次相当于没有翻转。实质上只用在一开始就储存原排列的时候进行翻转一次后另外储存,再进行旋转找最小字典序排列进行比较即可。

题解1:

#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int N = 1e5 + 10;
int n;
int snows[N][6], idx[N];

//判断两个数组的字典序(利用O(n)的复杂度进行查找最小的字典序排列)
bool cmp_array(int a[],int b[])
{
	for (int i = 0; i < 6;i++){
		if(a[i]>b[i])
			return 0;
		else if(a[i]<b[i])
			return 1;
	}
	return 0;
}

//判断以a,b所在位置为起点的排列是否相同
bool cmp(int a,int b)
{
	return cmp_array(snows[a], snows[b]);
}

//找字典序最小的排列
void get_min(int a[])
{
	static int b[12];
	for (int i = 0; i < 12;i++)
		b[i] = a[i % 6];
	int i = 0, j = 1, k;
	while(i<6&&j<6)
	{
		for (k = 0; k < 6 && b[i + k] == b[j + k];k++);
		if(k==6)
			break;
		if(b[i+k]>b[j+k])
		{
			i += k + 1;
			if(i==j)
				i++;
		}
		else{
			j += k + 1;
			if(i==j)
				j++;
		}
	}
	k = min(i, j);
	for (i = 0; i < 6;i++)
		a[i] = b[i + k];
}

int main()
{
	scanf("%d", &n);
	int snow[6], isnow[6];//原雪花和翻转后的雪花
	for (int i = 0; i < n;i++){
		for (int j = 0, k = 5; j < 6;j++,k--){
			scanf("%d", &snow[j]);
			isnow[k] = snow[j];
		}
		get_min(snow);//找到以原雪花为主序列的旋转后能够得到的字典序最小的排列
		get_min(isnow);//找到以翻转后的雪花为主序列的旋转后能够得到的字典序最小的排列
		if(cmp_array(snow,isnow))
			memcpy(snows[i], snow, sizeof snow);
		else
			memcpy(snows[i], isnow, sizeof isnow);
		idx[i] = i;
}
	sort(idx, idx + n, cmp);
	bool flag = 0;
	for (int i = 1; i < n;i++){
		if(!cmp(idx[i-1],idx[i])&&!cmp(idx[i],idx[i-1])){
			flag = 1;
			break;
		}
	}
		if (flag)
			puts("Twin snowflakes found.");
		else
			puts("No two snowflakes are alike.");
		return 0;
}

思路:如果两个排列相同,那么两者的和与积之和一定相等。先设置一个比较大的质数作为哈希表的大小,方便将数据储存以便能够通过某种映射关系以O(1)的复杂度查找到该数据。

题解2:
Hash表

#include<iostream>
#include<cstring>
using namespace std;

const int N = 1e5 + 10;
int n, tot, p = 99991, snow[N][6], head[N], Next[N];//建立链表。每次查找直接查找表头即可

//返回该情况的和与积之和
int Hash(int a[])
{
	int sum = 0, mul = 1;
	for (int i = 0; i < 6;i++){
		sum = (sum + a[i]) % p;
		mul = (long long)mul * a[i] % p;
	}
	return (sum + mul) % p;
}

//判断两者是否相同
bool equal(int a[],int b[])
{
	for (int i = 0; i < 6;i++){
		for (int j = 0; j < 6;j++){
			bool eq = 1;
			for (int k = 0; k < 6;k++)
				if(a[(i+k)%6]!=b[(j+k)%6])
					eq = 0;
			if(eq)
				return 1;
			eq = 1;
			for (int k = 0; k < 6;k++)
				if(a[(i+k)%6]!=b[(j-k+6)%6])
					eq = 0;
			if(eq)
				return 1;
		}
	}
	return 0;
}

bool insert(int a[])
{
	int val = Hash(a);
	for (int i = head[val]; i;i=Next[i])
		if(equal(snow[i],a))
			return 1;
	++tot;
	memcpy(snow[tot], a, 6 * sizeof(int));
	Next[tot] = head[val];
	head[val] = tot;
	return 0;
}
int main()
{
	cin >> n;
	for (int i = 1; i < 6;i++){
		int a[10];
		for (int j = 0; j < 6;j++){
			scanf("%d", &a[j]);
			if(insert(a)){
				puts("Twin snowflakes found.");
				return 0;
			}
		}
	}
	puts("No two snowflakes are alike.");
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值