题面
输入样例
2
1 2 3 4 5 6
4 3 2 1 6 5
输出样例
Twin snowflakes found.
题解(最小表示法)
- 给你N片雪花的六个角长度,然后问你这N片雪花中是否有形状相同的两片,最直接的办法就是暴力枚举,但是考虑到翻转和变化,暴力是不现实的 ,我们可以用hash来做,可以用一个哈希值来表示每一片雪花,当两个hash值相等时,就说明两片雪花是相同的,显然对于形状相同的雪花,他们的六角的长度之和,长度之积都相等,那么我们就将它的和,积相加在模一个较大的质数来表示其hash值
- 此题我们采用更好的一种方法来提高判断两片雪花形状是否相同的效率,那就是循环同构串的最小表示法 定义 :长度为n的序列通过旋转n次,这n个序列中字典序最小的就是最小表示 例如 4 3 2 1 6 5 的最小表示就是 1 6 5 4 3 2
- 对于此题,有两种操作方式,一种是翻转,另一种是移动 ,就是 1 2 3 4 5 6 可以翻转成 6 5 4 3 2 1 ,也可以移动 5 4 3 2 1 6 ,继续移动 4 3 2 1 6 5 我们取翻转前后的两次的一个最小表示作为这个雪花的唯一标识,那么有相同的最小表示我们就认为这两片雪花相同
- 最小表示法 详解 -----> 点这里,会的自行跳过
代码
#include<iostream>
#include<cstdio>
#include<string>
#include<cstring>
#include<algorithm>
using namespace std;
const int N = 1e5 + 10;
int n;
int snows[N][6], idx[N];
//最小表示模板
void get_min(int *b) {
int a[12];
for (int i = 0; i < 12; i++) a[i] = b[i % 6];
int i = 0, j = 1, k;
while (i < 6 && j < 6) {
for (k = 0; k < 6 && a[i + k] == a[j + k]; k++);
if (k == 6) break;
if (a[i + k] > a[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++) b[i] = a[i + k];
}
bool cmp1(int a[], int b[]) {
for (int i = 0; i < 6; i++) {
if (a[i] < b[i]) {
return true;
} else if (a[i] > b[i]) {
return false;
}
}
return false;
}
bool cmp2(int a, int b) {
for (int i = 0; i < 6; i++) {
if (snows[a][i] < snows[b][i]) {
return true;
} else if (snows[a][i] > snows[b][i]) {
return false;
}
}
return false;
}
int main() {
cin >> n;
int snow[6], isnow[6];
for (int i = 0; i < n; i++) {
for (int j = 0, k = 5; j < 6; j++, k--) {
cin >> snow[j];
isnow[k] = snow[j];
}
get_min(snow);
get_min(isnow);
//求出两个最小表示取其最小
if (cmp1(snow, isnow)) memcpy(snows[i], snow, sizeof snow);
else memcpy(snows[i], isnow, sizeof isnow);
idx[i] = i;
}
sort(idx, idx + n, cmp2);
bool flag = false;
for (int i = 1; i < n; i++) { //判断是否有相同的最小表示
if (!cmp2(idx[i], idx[i - 1]) && !cmp2(idx[i - 1], idx[i])) {
flag = true;
break;
}
}
if (flag) cout << "Twin snowflakes found." << endl;
else cout << "No two snowflakes are alike." << endl;
return 0;
}