• 算法趣题-Q33


    一、问题描述

    二、问题分析

            直接遍历统计,但是能够发现有部分是重复的,需要把问题分成4个相同的小问题,在边长为9的时候,分成4个4*5的矩形和最中间的那个点。把飞或者角固定在这个小矩形范围内,另一个棋子则可以无限制在棋盘的任意位置,这样将小矩形结果乘以4再加上中间的的可能就可以得到结果,还能减少重复计算。

            在对格子的计算中,一种方式是先填标记然后再重新遍历计数,如Python实现,显然,这种方法的效率不高,我们可以将遍历过的格子填上1(初始化为0),然后分别记录遍历过的格子的数目count和遍历时格子中的所有值的和sum,可以发现sum就是重复计数的部分。那么count-sum就是没有重复的格子计数值,如C/C++实现。

            最后在矩阵的数组表示中,我们采用此书提供的新思路,用给数组添加边界的方式去取代对数组下标的边界判断。

    三、代码实现

    1.C/C++实现

    1. #include
    2. #include
    3. #include
    4. #include
    5. #include
    6. #include
    7. #include
    8. using namespace std;
    9. const int SQUARE_LEN = 9;
    10. const int ARRAY_SIZE = SQUARE_LEN + 2;
    11. const int FLY = -2; // 飞用 -2 标识
    12. const int KOK = -3; // 角用 -3 标识
    13. // 飞的移动方向
    14. const int FLY_DET_X[] = { 0, 0, -1, 1 };
    15. const int FLY_DET_Y[] = { 1, -1, 0, 0 };
    16. // 角的移动方向
    17. const int KOK_DET_X[] = { -1, -1, 1, 1 };
    18. const int KOK_DET_Y[] = { -1, 1, -1, 1 };
    19. int square[ARRAY_SIZE][ARRAY_SIZE];
    20. void init_square()
    21. {
    22. memset(square, 0, sizeof(square)); // 全部置为 0
    23. // 将边界置为 -1
    24. for (int i = 0; i < SQUARE_LEN + 2; i++)
    25. {
    26. square[0][i] = square[ARRAY_SIZE - 1][i] = -1;
    27. square[i][0] = square[i][ARRAY_SIZE - 1] = -1;
    28. }
    29. }
    30. void show_square()
    31. {
    32. for (int i = 0; i < ARRAY_SIZE; i++)
    33. {
    34. for (int j = 0; j < ARRAY_SIZE; j++)
    35. cout << square[i][j] << '\t';
    36. cout << endl;
    37. }
    38. cout << endl;
    39. }
    40. int get_count(int fly_x, int fly_y, int kok_x, int kok_y)
    41. {
    42. init_square();
    43. square[fly_x][fly_y] = FLY;
    44. square[kok_x][kok_y] = KOK;
    45. int count = 0, redundant = 0;
    46. // 统计飞的格子
    47. for (int i = 0; i < 4; i++)
    48. {
    49. int tmp_x = fly_x + FLY_DET_X[i], tmp_y = fly_y + FLY_DET_Y[i];
    50. while (square[tmp_x][tmp_y] >= 0) // 遇见边界(-1)或另一个棋子停下
    51. {
    52. count++;
    53. square[tmp_x][tmp_y] = 1; // 用 1 标识 filled
    54. tmp_x += FLY_DET_X[i];
    55. tmp_y += FLY_DET_Y[i];
    56. }
    57. }
    58. // 统计角的格子
    59. for (int i = 0; i < 4; i++)
    60. {
    61. int tmp_x = kok_x + KOK_DET_X[i], tmp_y = kok_y + KOK_DET_Y[i];
    62. while (square[tmp_x][tmp_y] >= 0) // 遇见边界(-1)或另一个棋子停下(-2,-3)
    63. {
    64. count++;
    65. redundant += square[tmp_x][tmp_y];
    66. square[tmp_x][tmp_y] = 1; // 用 1 标识 filled
    67. tmp_x += KOK_DET_X[i];
    68. tmp_y += KOK_DET_Y[i];
    69. }
    70. }
    71. return count - redundant;
    72. }
    73. int main()
    74. {
    75. int total_count = 0;
    76. int mid1 = (SQUARE_LEN + 1) / 2;
    77. int mid2 = SQUARE_LEN / 2;
    78. // 计算飞在 1/4 个正方形的可能
    79. for (int fly_x = 1; fly_x <= mid1; fly_x++)
    80. for (int fly_y = 1; fly_y <= mid2; fly_y++)
    81. for (int kok_x = 1; kok_x <= SQUARE_LEN; kok_x++)
    82. for (int kok_y = 1; kok_y <= SQUARE_LEN; kok_y++)
    83. {
    84. if (kok_x == fly_x && kok_y == fly_y)
    85. continue;
    86. total_count += get_count(fly_x, fly_y, kok_x, kok_y);
    87. }
    88. total_count <<= 2; // total_count * 4
    89. // 当正方形边长是奇数时
    90. if (SQUARE_LEN % 2)
    91. {
    92. for (int kok_x = 1; kok_x <= SQUARE_LEN; kok_x++)
    93. for (int kok_y = 1; kok_y <= SQUARE_LEN; kok_y++)
    94. {
    95. if (kok_x == mid1 && kok_x == kok_y)
    96. continue;
    97. total_count += get_count(mid1, mid1, kok_x, kok_y);
    98. }
    99. }
    100. cout << total_count << endl;
    101. return 0;
    102. }

    2.Python实现

    1. # coding = utf-8
    2. SQUARE_SIZE = 9
    3. LIST_SIZE = SQUARE_SIZE + 2
    4. FLY_DXY = ((0, 1), (0, -1), (1, 0), (-1, 0))
    5. KOK_DXY = ((1, 1), (1, -1), (-1, 1), (-1, -1))
    6. def get_count(fly_x, fly_y, kok_x, kok_y):
    7. # 初始化列表
    8. board = [[0] * LIST_SIZE for i in range(LIST_SIZE)]
    9. board[0] = board[LIST_SIZE - 1] = [-1] * LIST_SIZE
    10. for i in range(LIST_SIZE):
    11. board[i][0] = board[i][LIST_SIZE - 1] = -1
    12. # 当前两个棋子也赋值负数
    13. board[fly_x][fly_y] = board[kok_x][kok_y] = -1
    14. # 给格子作标记
    15. for dx, dy in FLY_DXY:
    16. x, y = fly_x + dx, fly_y + dy
    17. while board[x][y] >= 0:
    18. board[x][y] = 1
    19. x, y = x + dx, y + dy
    20. # 给角的格子作标记
    21. for dx, dy in KOK_DXY:
    22. x, y = kok_x + dx, kok_y + dy
    23. while board[x][y] >= 0:
    24. board[x][y] = 1
    25. x, y = x + dx, y + dy
    26. return sum((sum(item) for item in board)) + 4 * (LIST_SIZE - 1) + 2
    27. def get_total_count():
    28. total_count = 0
    29. mid1 = (SQUARE_SIZE + 1) // 2
    30. mid2 = SQUARE_SIZE // 2
    31. for fly_x in range(1, mid1 + 1):
    32. for fly_y in range(1, mid2 + 1):
    33. for kok_x in range(1, SQUARE_SIZE + 1):
    34. for kok_y in range(1, SQUARE_SIZE + 1):
    35. if kok_x == fly_x and kok_y == fly_y:
    36. continue
    37. total_count += get_count(fly_x, fly_y, kok_x, kok_y)
    38. total_count <<= 2
    39. if SQUARE_SIZE % 2:
    40. fly_x = fly_y = mid1
    41. for kok_x in range(1, SQUARE_SIZE + 1):
    42. for kok_y in range(1, SQUARE_SIZE + 1):
    43. if kok_x == fly_x and kok_y == fly_y:
    44. continue
    45. total_count += get_count(fly_x, fly_y, kok_x, kok_y)
    46. return total_count
    47. if __name__ == '__main__':
    48. print(get_total_count())
    49. pass

  • 相关阅读:
    手机储存快满了,怎么备份手机图片和视频到电脑上?
    Java代码的编译过程(没写完,不要点进来)
    如果企业微信不用了怎么解绑手机?
    c++day4
    问题记录1
    【一文清晰】单元测试到底是什么?应该怎么做?
    Rust WASM 与 JS 计算素数性能对比
    MySQL索引理解
    Linux进程概念(下)
    LCR 147.最小栈
  • 原文地址:https://blog.csdn.net/qq_41893962/article/details/126055818