• 《算法竞赛进阶指南》 雪花雪花雪花


    有 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

    输入样例:

    1. 2
    2. 1 2 3 4 5 6
    3. 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;
            最后用双指针依次枚举两个数组,找出最小字典序;
        }
        

    解题代码:

    1. #include <cmath>
    2. #include <cstdio>
    3. #include <cstring>
    4. #include <iostream>
    5. #include <algorithm>
    6. using namespace std;
    7. const int N = 100010;
    8. int n;
    9. int snow[6], resnow[6];//snow为原数组,resnow为翻转数组
    10. int snows[N][6], idx[N];//snows记录的是每片雪花的最小字典序,idx用来将 snows排序
    11. void get_min(int b[])
    12. {
    13. static int a[12];
    14. for (int i = 0; i < 12; i ++ ) a[i] = b[i % 6];//把b变为长度是2 * len(b)的数组
    15. int i = 0, j = 1, k;//i为b1开始枚举的位置,j为b2开始枚举的位置,k为枚举的长度
    16. while (i < 6 && j < 6)//不能大于6,若大于6则不是一个完整的序列
    17. {
    18. for (k = 0; k < 6 && a[i + k] == a[j + k]; k ++ );//当他们当前比较的数字相等时两个指针同时往后移动一位
    19. if (k == 6) break;//数组最大长度为6,若k == 6说明找到了最小字典序
    20. if (a[i + k] > b[j + k])//说明从数组a前面从第i位数到第i + k位数中所对应的每个长度位n的序列都大于b
    21. {//这与找最小字典序不符,所以a应跳到第k位数后面继续枚举序列,即i += k + 1;
    22. i += k + 1;
    23. if (i == j) i ++ ;//因为是相同的数组,指针不能在一个时刻指向同一个位置。
    24. }
    25. else
    26. {
    27. j += k + 1;//同理
    28. if (i == j) j ++ ;
    29. }
    30. }
    31. k = min(i, j);//循环结束说明i, j中有一个大于6,要找出i, j中小于6的即求i, j最小值
    32. for (int i = 0; i < 6; i ++ ) b[i] = a[i + k];
    33. }
    34. bool cmp2(int a, int b)
    35. {
    36. for (int i = 0; i < 6; i ++ )
    37. if (snows[a][i] < snows[b][i])
    38. return true;
    39. else if (snows[a][i] > snows[b][i])
    40. return false;
    41. return false;
    42. }
    43. bool cmp(int a[], int b[])
    44. {
    45. for (int i = 0; i < 6; i ++ )
    46. if (a[i] < b[i])
    47. return true;
    48. else if (a[i] > b[i])
    49. return false;
    50. return false;
    51. }
    52. int main()
    53. {
    54. cin >> n;
    55. for (int i = 0; i < n; i ++ )
    56. {
    57. for (int j = 0, k = 5; j < 6; j ++, k -- )
    58. {
    59. scanf("%d", &snow[j]);//记录原数组与翻转数组
    60. resnow[k] = snow[j];
    61. }
    62. get_min(snow);//得到最小字典序
    63. get_min(resnow);
    64. if (cmp(snow, resnow)) memcpy(snows[i], snow, sizeof snow);
    65. else memcpy(snows[i], resnow, sizeof resnow);
    66. //比较原数组与翻转数组最小字典序的大小,将最小字典序最小的存下来
    67. idx[i] = i;
    68. }
    69. sort(idx, idx + n, cmp2);//将所有snows从小到大排序
    70. bool is_same = false;
    71. for (int i = 1; i < n; i ++ )
    72. if (!cmp2(idx[i - 1], idx[i]) && !cmp2(idx[i], idx[i - 1]))
    73. //若idx[i - 1] >= idx[i] && idx[i] >= idx[i - 1]则两者相等
    74. {
    75. is_same = true;
    76. break;
    77. }
    78. if (is_same) puts("Twin snowflakes found.");
    79. else puts("No two snowflakes are alike.");
    80. return 0;
    81. }

     

  • 相关阅读:
    阿里云全站加速 DCDN 重磅发布!打造新一代加速引擎
    ajax异步访问及跨域处理
    Scala面向对象
    Codeforces Round 953 (Div. 2)(A~D题解)
    【Andriod】Appium的不同版本(Appium GUI、Appium Desktop、Appium Server )的安装教程
    基于LSTM时间序列预测(简单又好用)无脑代码
    【UE C++】 虚幻引擎C++开发需要掌握的C++和U++的基础知识有哪些?
    基于Matlab实现图像分割技术(附上源码+图像)
    Oracle数据库:创建表空间,创建新用户,给用户分配对象、角色和系统权限,登录新用户建表
    密度估计公式
  • 原文地址:https://blog.csdn.net/qq_61935738/article/details/125634059