有 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;
- }