算法趣题-Q33

一、问题描述

二、问题分析

        直接遍历统计,但是能够发现有部分是重复的,需要把问题分成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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值