有 NN 片雪花,每片雪花由六个角组成,每个角都有长度。
第 ii 片雪花六个角的长度从某个角开始顺时针依次记为 ai,1,ai,2,…,ai,6ai,1,ai,2,…,ai,6。
因为雪花的形状是封闭的环形,所以从任何一个角开始顺时针或逆时针往后记录长度,得到的六元组都代表形状相同的雪花。
例如 ai,1,ai,2,…,ai,6ai,1,ai,2,…,ai,6 和 ai,2,ai,3,…,ai,6,ai,1ai,2,ai,3,…,ai,6,ai,1 就是形状相同的雪花。
ai,1,ai,2,…,ai,6ai,1,ai,2,…,ai,6 和 ai,6,ai,5,…,ai,1ai,6,ai,5,…,ai,1 也是形状相同的雪花。
我们称两片雪花形状相同,当且仅当它们各自从某一角开始顺时针或逆时针记录长度,能得到两个相同的六元组。
求这 NN 片雪花中是否存在两片形状相同的雪花。
输入格式
第一行输入一个整数 NN,代表雪花的数量。
接下来 NN 行,每行描述一片雪花。
每行包含 66 个整数,分别代表雪花的六个角的长度(这六个数即为从雪花的随机一个角顺时针或逆时针记录长度得到)。
同行数值之间,用空格隔开。
输出格式
如果不存在两片形状相同的雪花,则输出:
No two snowflakes are alike.
如果存在两片形状相同的雪花,则输出:
Twin snowflakes found.
数据范围
1≤N≤1000001≤N≤100000,
0≤ai,j<100000000≤ai,j<10000000输入样例:
2 1 2 3 4 5 6 4 3 2 1 6 5
输出样例:
Twin snowflakes found.
先补充一个函数:
补充一个函数sort(a, a + n, cmp);
bool cmp(int a, int b)
{
if (a < b) return true:(返回的是a序列从小到大排序)
}
bool cmp(int a, int b)
{
if (a > b) return true:(返回的是a序列从大到小排序)
}
解题思路:
每次求出每个序列的最小字典序,若每个最小字典序列中存在相同的序列则说明存在相同的雪花
如何求得最小字典序呢:
{
方法一:1:将每个序列的最前一位依次移动到最后一个位置,比如一个长度为4的序列:
1,2,3,4移动后2,3,4,1直到序列变为4,3,2,得到每次变化过程中的序列的某一次的最小的字典序;
方法二:求翻转序列:将一个序列翻转,比如一个长度为4的序列5, 2, 3, 4翻转后即为4, 3, 2, 5;
将前面两种方法结合即:先求翻转序列然后求原序列,以及翻转序列,每次将首位元素移动到末尾元素的最小字典序
最后将翻转序列的最小字典序与原序列的最小字典序比较,得出的答案即为该序列的最小字典序
求最小字典序的方法:
{
可以定义一个长度为2 * n的序列记录的是由原序列(即为b[N])复制过来长度是原序列两倍的序列(即为a[N]):
例如原序列a[N]为5, 2, 3, 4的b[N * 2]为5, 2, 3, 4, 5, 2, 3, 4;
然后将b[N * 2]分为:
b1[N * 2]:5, 2, 3, 4, 5, 2, 3, 4;
b2[N * 2]:5, 2, 3, 4, 5, 2, 3, 4;
最后用双指针依次枚举两个数组,找出最小字典序;
}
}
解题代码:
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 100010;
int n;
int snow[6], resnow[6];//snow为原数组,resnow为翻转数组
int snows[N][6], idx[N];//snows记录的是每片雪花的最小字典序,idx用来将 snows排序
void get_min(int b[])
{
static int a[12];
for (int i = 0; i < 12; i ++ ) a[i] = b[i % 6];//把b变为长度是2 * len(b)的数组
int i = 0, j = 1, k;//i为b1开始枚举的位置,j为b2开始枚举的位置,k为枚举的长度
while (i < 6 && j < 6)//不能大于6,若大于6则不是一个完整的序列
{
for (k = 0; k < 6 && a[i + k] == a[j + k]; k ++ );//当他们当前比较的数字相等时两个指针同时往后移动一位
if (k == 6) break;//数组最大长度为6,若k == 6说明找到了最小字典序
if (a[i + k] > b[j + k])//说明从数组a前面从第i位数到第i + k位数中所对应的每个长度位n的序列都大于b
{//这与找最小字典序不符,所以a应跳到第k位数后面继续枚举序列,即i += k + 1;
i += k + 1;
if (i == j) i ++ ;//因为是相同的数组,指针不能在一个时刻指向同一个位置。
}
else
{
j += k + 1;//同理
if (i == j) j ++ ;
}
}
k = min(i, j);//循环结束说明i, j中有一个大于6,要找出i, j中小于6的即求i, j最小值
for (int i = 0; i < 6; i ++ ) b[i] = a[i + k];
}
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;
}
bool cmp(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;
}
int main()
{
cin >> n;
for (int i = 0; i < n; i ++ )
{
for (int j = 0, k = 5; j < 6; j ++, k -- )
{
scanf("%d", &snow[j]);//记录原数组与翻转数组
resnow[k] = snow[j];
}
get_min(snow);//得到最小字典序
get_min(resnow);
if (cmp(snow, resnow)) memcpy(snows[i], snow, sizeof snow);
else memcpy(snows[i], resnow, sizeof resnow);
//比较原数组与翻转数组最小字典序的大小,将最小字典序最小的存下来
idx[i] = i;
}
sort(idx, idx + n, cmp2);//将所有snows从小到大排序
bool is_same = false;
for (int i = 1; i < n; i ++ )
if (!cmp2(idx[i - 1], idx[i]) && !cmp2(idx[i], idx[i - 1]))
//若idx[i - 1] >= idx[i] && idx[i] >= idx[i - 1]则两者相等
{
is_same = true;
break;
}
if (is_same) puts("Twin snowflakes found.");
else puts("No two snowflakes are alike.");
return 0;
}