一、问题描述
二、问题分析
直接遍历统计,但是能够发现有部分是重复的,需要把问题分成4个相同的小问题,在边长为9的时候,分成4个4*5的矩形和最中间的那个点。把飞或者角固定在这个小矩形范围内,另一个棋子则可以无限制在棋盘的任意位置,这样将小矩形结果乘以4再加上中间的的可能就可以得到结果,还能减少重复计算。
在对格子的计算中,一种方式是先填标记然后再重新遍历计数,如Python实现,显然,这种方法的效率不高,我们可以将遍历过的格子填上1(初始化为0),然后分别记录遍历过的格子的数目count和遍历时格子中的所有值的和sum,可以发现sum就是重复计数的部分。那么count-sum就是没有重复的格子计数值,如C/C++实现。
最后在矩阵的数组表示中,我们采用此书提供的新思路,用给数组添加边界的方式去取代对数组下标的边界判断。
三、代码实现
1.C/C++实现
#include <string>
#include <sstream>
#include <algorithm>
#include <iostream>
#include <cstdio>
#include <cmath>
#include <cctype>
using namespace std;
const int SQUARE_LEN = 9;
const int ARRAY_SIZE = SQUARE_LEN + 2;
const int FLY = -2; // 飞用 -2 标识
const int KOK = -3; // 角用 -3 标识
// 飞的移动方向
const int FLY_DET_X[] = { 0, 0, -1, 1 };
const int FLY_DET_Y[] = { 1, -1, 0, 0 };
// 角的移动方向
const int KOK_DET_X[] = { -1, -1, 1, 1 };
const int KOK_DET_Y[] = { -1, 1, -1, 1 };
int square[ARRAY_SIZE][ARRAY_SIZE];
void init_square()
{
memset(square, 0, sizeof(square)); // 全部置为 0
// 将边界置为 -1
for (int i = 0; i < SQUARE_LEN + 2; i++)
{
square[0][i] = square[ARRAY_SIZE - 1][i] = -1;
square[i][0] = square[i][ARRAY_SIZE - 1] = -1;
}
}
void show_square()
{
for (int i = 0; i < ARRAY_SIZE; i++)
{
for (int j = 0; j < ARRAY_SIZE; j++)
cout << square[i][j] << '\t';
cout << endl;
}
cout << endl;
}
int get_count(int fly_x, int fly_y, int kok_x, int kok_y)
{
init_square();
square[fly_x][fly_y] = FLY;
square[kok_x][kok_y] = KOK;
int count = 0, redundant = 0;
// 统计飞的格子
for (int i = 0; i < 4; i++)
{
int tmp_x = fly_x + FLY_DET_X[i], tmp_y = fly_y + FLY_DET_Y[i];
while (square[tmp_x][tmp_y] >= 0) // 遇见边界(-1)或另一个棋子停下
{
count++;
square[tmp_x][tmp_y] = 1; // 用 1 标识 filled
tmp_x += FLY_DET_X[i];
tmp_y += FLY_DET_Y[i];
}
}
// 统计角的格子
for (int i = 0; i < 4; i++)
{
int tmp_x = kok_x + KOK_DET_X[i], tmp_y = kok_y + KOK_DET_Y[i];
while (square[tmp_x][tmp_y] >= 0) // 遇见边界(-1)或另一个棋子停下(-2,-3)
{
count++;
redundant += square[tmp_x][tmp_y];
square[tmp_x][tmp_y] = 1; // 用 1 标识 filled
tmp_x += KOK_DET_X[i];
tmp_y += KOK_DET_Y[i];
}
}
return count - redundant;
}
int main()
{
int total_count = 0;
int mid1 = (SQUARE_LEN + 1) / 2;
int mid2 = SQUARE_LEN / 2;
// 计算飞在 1/4 个正方形的可能
for (int fly_x = 1; fly_x <= mid1; fly_x++)
for (int fly_y = 1; fly_y <= mid2; fly_y++)
for (int kok_x = 1; kok_x <= SQUARE_LEN; kok_x++)
for (int kok_y = 1; kok_y <= SQUARE_LEN; kok_y++)
{
if (kok_x == fly_x && kok_y == fly_y)
continue;
total_count += get_count(fly_x, fly_y, kok_x, kok_y);
}
total_count <<= 2; // total_count * 4
// 当正方形边长是奇数时
if (SQUARE_LEN % 2)
{
for (int kok_x = 1; kok_x <= SQUARE_LEN; kok_x++)
for (int kok_y = 1; kok_y <= SQUARE_LEN; kok_y++)
{
if (kok_x == mid1 && kok_x == kok_y)
continue;
total_count += get_count(mid1, mid1, kok_x, kok_y);
}
}
cout << total_count << endl;
return 0;
}
2.Python实现
# coding = utf-8
SQUARE_SIZE = 9
LIST_SIZE = SQUARE_SIZE + 2
FLY_DXY = ((0, 1), (0, -1), (1, 0), (-1, 0))
KOK_DXY = ((1, 1), (1, -1), (-1, 1), (-1, -1))
def get_count(fly_x, fly_y, kok_x, kok_y):
# 初始化列表
board = [[0] * LIST_SIZE for i in range(LIST_SIZE)]
board[0] = board[LIST_SIZE - 1] = [-1] * LIST_SIZE
for i in range(LIST_SIZE):
board[i][0] = board[i][LIST_SIZE - 1] = -1
# 当前两个棋子也赋值负数
board[fly_x][fly_y] = board[kok_x][kok_y] = -1
# 给格子作标记
for dx, dy in FLY_DXY:
x, y = fly_x + dx, fly_y + dy
while board[x][y] >= 0:
board[x][y] = 1
x, y = x + dx, y + dy
# 给角的格子作标记
for dx, dy in KOK_DXY:
x, y = kok_x + dx, kok_y + dy
while board[x][y] >= 0:
board[x][y] = 1
x, y = x + dx, y + dy
return sum((sum(item) for item in board)) + 4 * (LIST_SIZE - 1) + 2
def get_total_count():
total_count = 0
mid1 = (SQUARE_SIZE + 1) // 2
mid2 = SQUARE_SIZE // 2
for fly_x in range(1, mid1 + 1):
for fly_y in range(1, mid2 + 1):
for kok_x in range(1, SQUARE_SIZE + 1):
for kok_y in range(1, SQUARE_SIZE + 1):
if kok_x == fly_x and kok_y == fly_y:
continue
total_count += get_count(fly_x, fly_y, kok_x, kok_y)
total_count <<= 2
if SQUARE_SIZE % 2:
fly_x = fly_y = mid1
for kok_x in range(1, SQUARE_SIZE + 1):
for kok_y in range(1, SQUARE_SIZE + 1):
if kok_x == fly_x and kok_y == fly_y:
continue
total_count += get_count(fly_x, fly_y, kok_x, kok_y)
return total_count
if __name__ == '__main__':
print(get_total_count())
pass