八数码问题

2023-05-16

  • I. 问题介绍

八数码问题是一种经典的智力游戏,也是一种搜索算法的经典案例。该问题是指在一个3x3的棋盘上,有8个方块和一个空格,每个方块上标有1~8的数字,空格可以和相邻的方块交换位置,目标是通过交换方块的位置,使得棋盘上的数字排列成目标状态。

对于八数码在程序中的处理,我们通过拉直的方式,将八数码棋盘拉成一条字符串,用0来表示空位,用1-8的数字来表示相应的数码。这样一来就可以将二维的矩阵作为一维的向量处理,大大简化了问题。通过移动规则,用一维字符串上的索引来表示二维矩阵中的移动规则。

我们可以通过移动规则,用来产生某种棋盘可移动的所有情况。某一个节点下均连接了若干个子节点,这就形成了一个逻辑上的树结构。

既然八数码的移动可以看作为一个树结构,那么我们就可以对这个树进行搜索。搜索方式为广度优先搜索、宽度优先搜索、启发式搜索。

  • II. 思路设计

程序使用open表与close表来组织节点的搜索。open表用来保存待搜索的节点,close表用来保存以搜索的节点。实现搜索本质上是对open表与close表进行维护,两者中更重要的是维护好open表。我们可以通过open表中不同的取值方式,来实现广度优先搜索、宽度优先搜索与启发式搜索。

下图为open表与close表的示意图,先将open表中的某一节点取出来,判断其是否为目标节点,若不是目标节点,则将其展开,展开时要判断子节点是否再close表中,避免重复搜索,并将其子节点加入open表的尾部,再把展开的节点丢入close表,这就是搜索的基本过程。

 

搜索的关键在于open表中的节点如何取,这也是实现不同搜索方式的关键。

  • 广度优先搜索

广度优先搜索的实现方式为:每次弹出open表中的第一个节点。如下图所示,在节点1展开完成后,open表继续弹出第一个节点,也就是节点2,节点2展开又形成新的节点5与节点6;当节点2完成展开后,open表将会弹出节点3;节点3完成展开后,open表将会弹出节点4;当节点2、3、4均完成展开后,接下来再展开节点5,然后是节点6……。

子节点将会在open表的尾部不断累积,要先将open表前面的父节点遍历完成后,才能开始遍历子节点。通过这种方式,实现了广度优先搜索。

 

  • 深度优先搜索

深度优先搜索的实现方式为:每次弹出open表的最后一个节点。如下图所示,在节点1完成展开后,open表弹出最后一个节点4,节点4展开形成节点5、6,并将其加入到open表的尾部,接下来将会弹出最后一个节点6,节点6的子节点又会加入到open表的尾部,接下来继续弹出最后一个节点,这个节点则是节点6的子节点。

子节点将会在open表的尾部不断弹出,每次都向更深层次的子节点搜索,这样就实现了深度遍历搜索。

  • 启发式搜索

广度优先搜索与深度优先搜索虽然是有序的搜索,但其本质上还是盲目的搜索,搜索过程中程序并不知道目标在什么地方。启发式搜索其实就是告诉了程序,目标在哪里,让程序倾向于朝着目标距离小的方搜索。

如下图所示,启发式搜索在展开节点时,会先计算节点与目标之间的距离,并在下一步优先展开距离小的节点。这样一来程序就会避免展开很多不必要的节点,极大提高程序的搜索效率。

 

我们需要做的是定义一个代价函数,用来计算节点与目标节点之间的距离,这个距离也被称为代价。在open表弹出节点时,将会弹出代价最小的节点,一步步靠进目标节点。

计算距离的函数有很多,我定义了三种代价函数,一种是字符串对位距离,一种是曼哈顿距离,一种是欧式距离。我认为字符串距离存在着缺陷,比如索引2与索引3,他们在字符串中的距离只有1,但是在实际的网格中,他们的距离是大于1的,因此我个人更加倾向于曼哈顿距离与欧式距离,实践证明亦是如此。

  • III. 代码实现

在代码的实现中,我将广度优先搜索、深度优先搜索、启发式搜索全部整合到了一个框架中,可以通过修改全局变量,实现不同的模式。

我在代码中做了许多鲁棒性的操作,本来的想法是,将八数码问题很容易的扩展到十五数码问题,但是后来才想到,代码处理节点的方式是将其拉成一条字符串,这样以来,10以上的数字就没有办法表示了,因为他们占了两位字符,只能放弃了这个想法。但把代码中的鲁棒性操作都保留了下来。

  •  check函数

用于检查用户的输入是否符合规范。若不符合,则抛出异常。

def check():
    """检查用户输入"""
    # 检查节点长度是否符合要求
    check_len = len(start) == len(target) and len(start) ** 0.5 % 1 == 0
    assert check_len, f'请检查开始节点与目标节点的长度是否合理 开始节点长度{len(start)} 目标节点长度{len(target)}'

    # 检查节点中的数字是否符合要求
    # 节点中的数字查全
    check_recall = lambda x: all([str(flag) in x for flag in range(int(len(start)))])
    assert check_recall(start) and check_recall(target), f'请检查开始节点与目标节点是否输入正确 开始节点{start} 目标节点{target}'

    # 检查搜索模式
    assert mode in ALLOW_MODE, f'搜索模式 {mode} 不支持'

    # 检查搜索方式
    if mode == ALLOW_MODE[2]:
        cod = list(cost_func.values())
        count = 0
        for i in cod:
            if i:
                count += 1
        # 只允许有一个True
        assert count == 1, f'请检查代价方程的选择是否合理, {cod}'

  • get_rules函数

本来的想法是将八数码扩展到更多数码的情况,因而使用get_rules产生空位的移动规则。函数的思想为:将字符串的索引还原至网格的坐标内,并获取上下左右四个方位的索引值,再将越界值删除,最后保存在字典内。

def get_rules():
    # 用于存放规则的字典
    rules = dict()
    # 字符串长度
    length = len(start)
    # 棋盘的边长
    size = int(length ** 0.5)
    # 遍历所有索引
    for i in range(length):
        # 将坐标还原至网格
        row, col = i // size, i % size
        # 获取四个方位的坐标
        up, down, left, right = i - size, i + size, i - 1, i + 1
        d = [up, down, left, right]
        # 若当前索引处于边界,删除越界值
        if row == 0:
            d.remove(up)
        if row == size - 1:
            d.remove(down)
        if col == 0:
            d.remove(left)
        if col == size - 1:
            d.remove(right)
        # 创建键值对
        rules[i] = d
    return rules

  • swap_puzzle函数

swap_puzzle函数用于交换两个数码的位置,实际上就是移动空位。参数node表示要进行交换操作的节点,i和j表示要交换哪两处的索引。字符串可以通过索引访问,但不可通过索引修改,因此要将其先转换为列表,交换完成后再还原为字符串。

def swap_puzzle(node, i, j):
    """交换数码盘"""
    temp = list(node)
    temp[i], temp[j] = temp[j], temp[i]
    return ''.join(temp)
  • expand函数

expand函数用于展开节点,根据空位的移动规则,生成某一节点的所有未遍历的子节点。参数info的格式如下:[节点,距离,展开次数],info不仅仅是一个节点,而是一个携带了节点与距离信息的列表。并在展开节点时,计算出各子节点与目标节点的距离。

def expand(info):
    node = info[0]
    zero_idx = node.index("0")
    infos = []
    for idx in RULES[zero_idx]:
        new_node = swap_puzzle(node, zero_idx, idx)
        if new_node not in close:
            infos.append([new_node, cost(new_node, **cost_func), info[2]+1])
    return infos

  • cost函数

用于计算代价,也就是当前节点与目标节点之间的距离。我在函数中提供了三种代价函数,一种是直接计算字符串的对位距离,一种是曼哈顿距离,一种是欧式距离。计算曼哈顿距离与欧式距离时,要先将索引还原到网格坐标中。

def cost(node, str_dis=True, mhd_dis=False, o_dis=False):
    """
    计算代价函数
    :param now: 字符串类型 表示当前节点
    :param str_dis: 布尔类型 表示是否使用字符串距离(将当前节点与目标节点的字符索引做差)
    :param mhd_dis: 布尔类型 表示是否曼哈顿距离(将字符串还原至网格坐标中)
    :param o_dis: 布尔类型 是否使用欧式距离(将字符串还原至网格坐标中)
    :return:
    """
    cost = 0

    # 遍历每个字符及其索引
    for i, char in enumerate(node):
        # 将网络视为为一条向量(字符串)
        # 用这条向量计算代价
        # "541203786"情况下遍历了310个节点
        if str_dis:
            cost += abs(i - target.index(char))

        # 0  1  2
        # 3  4  5
        # 6  7  8
        # 将字符串还原为网格,计算其曼哈顿距离
        # 曼哈顿距离为 x轴距离+y轴距离
        else:
            # 计算这个索引属于哪一行
            row = i // 3
            # 计算这个索引属于哪一列
            con = i % 3

            # 获取目标位置的行和列
            end_idx = target.index(char)

            end_row = end_idx // 3
            end_con = end_idx % 3

            # 计算曼哈顿距离
            # "541203786"情况下遍历了240个节点
            if mhd_dis:
                cost += abs(row-end_row) + abs(con-end_con)

            # 计算欧几里得距离(欧式距离)
            # "541203786"情况下遍历了214个节点
            elif o_dis:
                cost += ((row-end_row) ** 2 + (con-end_con) ** 2)**0.5

    return cost

  • print_node函数、find_path函数、final_print函数

这三个函数对于算法没有直接的意义,算是工具函数,是用来向控制台输出最后的移动路径的。这里在产生移动路径时,程序的搜索树的结构为,键为子节点,值为父节点,这种设计可以更方便的帮助程序找到移动路径。

def print_node(node):
    """将节点以特定格式输出"""
    length = len(start)
    size = int(length ** 0.5)
    for i in range(0, length, size):
        print('  '.join(node[i: i + size]))


def find_path():
    """  通过nodes找到路径 """
    path = []
    # 树的结构为 键为子节点 值为父节点
    son = target
    while True:
        father = tree[son]
        path.append(son)
        if father != -1:
            son = father
        else:
            break
    return path


def final_print():
    """最终的输出"""
    print("\n移动顺序如下")
    total_path = find_path()
    for path in total_path + [target]:
        print_node(path)
        print()
    print(f"遍历节点个数: {len(close)} 移动步数: {len(total_path)}")
    print(f"累计用时: {time.time() - start_time:.2f}秒")

  • 主程序

主程序中规定了全局变量,全局变量包括了初始节点,目标节点,模式设置等一系列参数,提供了多种选项与功能。

在程序的主循环中,实现了根据不同的模式弹出不同的节点。在启发式搜索弹出节点时,不仅仅参考了节点与目标节点之间的距离,还参考了展开次数。可以理解为,程序不仅要通过最短的距离找到目标节点,还要尽量减少节点展开的次数。

if __name__ == '__main__':

    # 开始节点
    start = "541203786"
    # start = "134082576"
    # 目标节点
    target = "123804765"

    # 是否随机打乱开始状态
    shuffle_start = False

    ALLOW_MODE = ['广度优先搜索', '深度优先搜索', '启发式搜索']
    # 搜索模式 支持的模式:0 广度优先搜索 1 深度优先搜索 2 启发式搜索
    mode = ALLOW_MODE[2]

    cost_func = {'str_dis': False, 'mhd_dis': False, 'o_dis': True}

    # open表
    open = []
    # close表
    close = []

    # 搜索树
    tree = dict()

    RULES = get_rules()

    # 检查变量是否设置正确
    check()

    print(f"搜索方式 {mode} ", end='')
    if mode == ALLOW_MODE[2]:
        print(f"{'字符串距离' if cost_func['str_dis'] else ''}", end='')
        print(f"{'曼哈顿距离' if cost_func['mhd_dis'] else ''}", end='')
        print(f"{'欧式距离' if cost_func['o_dis'] else ''}", end='')
    print()

    if shuffle_start:
        # 随机打乱初始节点
        start = list(start)
        random.shuffle(start)
        start = ''.join(start)

    print('初始节点')
    print_node(start)

    # 将第一个节点放入open表中 [节点,代价,步数]
    open.append([start, cost(start, **cost_func), 0])

    # tree['root'] = start
    tree[start] = -1

    # 获取程序开始时间
    start_time = time.time()
    while len(open) > 0:
        # 输出日志信息
        print(f"\r正在搜索...{time.time() - start_time:.0f}s 已遍历节点数:{len(close)}", end='')

        # 不同的模式弹出不同的节点
        if mode == ALLOW_MODE[0]:
            #
            info = open.pop(0)
        elif mode == ALLOW_MODE[1]:
            info = open.pop(-1)
        elif mode == ALLOW_MODE[2]:
            info = min(open, key=lambda x: x[1] + x[2])
            open.remove(info)

        # 获取节点值
        node = info[0]

        # 将节点保存至close表
        close.append(node)

        # 判断是否为目标节点
        if node == target:
            final_print()
            break

        # 展开节点
        infos = expand(info)
        open.extend(infos)

        for info in infos:
            tree[info[0]] = node

    else:
        # while循环正常结束,open表为空,所有节点搜索完毕
        print("问题无解")
        print(f"遍历节点个数: {len(close)}")
        print(f"累计用时: {time.time() - start_time:.2f}秒")

  • IV. 实验结果

对于541203786这种情况:

  • 盲目搜索

当使用广度优先搜索时,遍历节点72085个,移动步数21步,累计时间121.94秒。时间消耗与遍历节点数每个人可能不同,因为这个结果受到移动规则中的排列顺序影响较大。广度优先搜索移动步数较少,因为其按层搜索,每一层就是一个移动。

 

当使用深度优先搜索时,遍历节点45147个,移动步数43797步,累计时间61.71秒。深度优先搜索遍移动步数较多,因为其大多数情况展开一次就是一个移动。

 

上述两种为盲目搜索的实验结果,两种搜索中的运气成分还是比较多的。

  • 启发式搜索:

使用字符串对位距离:遍历节点数727个,移动步数23步,累计用时0.11秒。启发式相较于盲目搜索的几万个搜索节点,可以说是提升巨大,但代价函数不合适的情况下,可能会找到多余的移动步数。

 

曼哈顿距离:遍历节点数605个,移动步数21步,累计用时0.09秒。相较字符串的对位距离,曼哈顿距离显然在每个数值上都优于字符串对位距离,原因可能是前文提到的字符串对位距离的不合理性。

 

欧式距离:遍历节点数837个,移动步数21步,累计用时0.13秒。结果依然优于字符串对位距离,但要略差于曼哈顿距离。

 

  • V.总结

在八数码问题中,启发式搜索无疑碾压盲目搜索,但启发式搜索也不一定是快最优的解。报告仅针对一种初始状态做了实验,还需要更多的实验次数来支撑实验结果,还有更多的代价函数等待应用,此外,还可以通过调节展开次数与距离之间的占比,以获取更佳的效果。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

八数码问题 的相关文章

  • 输入一个0到999可带小数的数字,输出其个十百位数

    多组输入 int 表达式 xff09 指将表达式中的结果强制转换成整型 include lt stdio h gt int main double a int ge shi bai while scanf 34 lf 34 amp a ba
  • 计算2X4X6X...X100的值(上面是错解,附正解)

    include lt stdio h gt int main int a 61 2 n 61 1 sum 61 1 while n lt 61 50 n 61 n 43 1 sum 61 sum a a 61 a 43 2 printf 3
  • while循环输出100-200的所有整数

    include lt stdio h gt int main int n 61 100 while n lt 61 200 printf 34 d t 34 n n 43 43 return 0
  • 关于如何输出百分号和0.0f%的格式化字符的理解,输出结果为2.684%

    include lt stdio h gt int main int a 61 10433 b 61 280 float f1 f1 61 b 1 0 a 当乘以1 0后 xff0c 才能使算出f1是想要的结果 xff08 0 026838
  • 输入自然数后逆序输出这个数

    include lt stdio h gt int main int a scanf 34 d 34 amp a while a a不等于零时执行此循环 printf 34 d 34 a 10 a 61 a 10 return 0
  • LINUX最小系统安装过程中的Partition Disks分配问题

    问题详细描述 xff1a 在我们安装Debian最小系统 xff08 即双系统windows和linux xff09 过程中在进行内存分配时 xff0c 由于系统需要我们很多时候都得选用英文系统导致可能存在 xff1a 看不懂 不知道怎么进
  • mybatis-plus自定义多表分页查询

    mybati plus多表分页查询 首先编写VO类 xff0c VO类包含了要查询的字段值 xff0c 现在有如下几个表 blog表 span class token annotation punctuation 64 Data span
  • 【Docker学习笔记】2.Debian Docker 安装及CentOS Docker 安装

    前言 本章介绍Debian Docker 安装和CentOS Docker 安装 Debian Docker 安装 Docker 支持以下的 Debian 版本 xff1a 巴斯特 10拉伸 9 xff08 稳定 xff09 拉斯比亚拉伸
  • 超详细的操作符讲解

    操作符的分类 算术操作符 移位操作符 位操作符 赋值操作符 单目操作符 关系操作符 逻辑操作符 条件操作符 逗号表达式 1 算术操作符 span class token operator 43 span span class token o
  • Ubuntu 安装中文输入法

    请注意命令中不应该的空格可能导致命令不合法 xff01 一 检查 fcitx 框架 首先 xff0c 要安装中文输入法 xff0c 必须要保证系统上有 fcitx fcitx是一个以 GPL 方式发布的输入法框架 xff0c 安装 fcit
  • 快速幂取模简单用法

    关于快速幂的用法本小白还是思索了挺久的 xff08 因为我不太聪明哈 xff09 xff0c 但是学会了就觉得挺好理解的 下面我用一道例题来简单讲一下它的用法 xff0c 希望能帮你轻松get到 xff01 xff01 例题 xff1a 快
  • robomaster视觉规则细谈

    目录 攻击与检测 弹丸参数 增益点增益 升级效果 击打检测 涂装要求 裁判系统 机器人端各模块 赛事引擎各部分 客户端 服务器 能量机关 小能量机关 大能量机关 算法归纳 攻击与检测 弹丸参数 如图所示 xff0c 赛场中我们使用的弹丸有两
  • 关于如何用python下载文件

    先贴上源代码 xff1a GitHub zhangbaji PythonDownloader 这是一个Python下载http文件的事例 xff0c 只不过目前还无法获取动态文件的文件名 https github com zhangbaji
  • Java打印杨辉三角形

    span class token keyword public span span class token keyword class span span class token class name Taks02 span span cl
  • 第二章 利用ffmpeg把rgb转mp4

    本文章介绍ffmpeg基本使用流程 1 创建编码器 通过枚举id选择编码器类型 AVCodec avcodec find encoder enum AVCodecID id enum AVCodecID id 通过枚举选择编码器类型 AV
  • 栈的相关题目以及出栈时的规律

    今天做了两个有关栈的题目 xff0c 都可以用到出栈时的规律来解题 那么问题来了 xff0c 出栈时的规律是什么呢 xff1f 规律如下 xff1a 已知栈的输入序列是1 2 3 n xff0c 输出序列是a1 a2 ai an 然后我们任
  • Python 中 jieba 库

    文章目录 jieba库一 简介1 是什么2 安装 二 基本使用1 三种模式2 使用语法2 1 对词组的基本操作2 2 关键字提取2 3 词性标注2 4 返回词语在原文的起止位置 jieba库 一 简介 1 是什么 xff08 1 xff09
  • 【栈与队列】之栈的顺序存储(图文详细介绍!!)

    前言 xff1a 本章基于 大话数据结构 和王卓老师的视频内容 xff0c 为刚接触数据结构的初学者提供一些帮助 x1f495 如果我的文章对你有帮助 xff0c 点赞 收藏 留言都是对我最大的动力 目录 4 1 栈的定义 4 2 栈的抽象
  • 【C语言技能树】程序环境和预处理

    Halo xff0c 这里是Ppeua 平时主要更新C语言 xff0c C 43 43 xff0c 数据结构算法 感兴趣就关注我吧 xff01 你定不会失望 x1f308 个人主页 xff1a 主页链接 x1f308 算法专栏 xff1a
  • Java中输出所有的水仙花数

    问题描述 打印出所有的 水仙花数 xff0c 所谓 水仙花数 是指一个三位数 xff0c 其各位数字立方和等于该数本身 例如 xff1a 153是一个 水仙花数 xff0c 因为153 61 1的三次方 43 5的三次方 43 3的三次方

随机推荐

  • pip3 设置阿里云

    pip3 install r requirements txt 报超时 xff0c 于是设置阿里云作为安装源 xff1a pip3 config set global index url http mirrors aliyun com py
  • 输入一个数组,将其逆序输出

    今天参加了校内的计算机技能大赛 xff0c 找到了一道较为简单的题 xff0c 就是 将数组逆序输出 下面我将详细讲解一下代码思路 xff0c 好了 xff0c 老规矩 xff0c 先上代码 xff01 include lt bits st
  • 虚拟机中Ubuntu安装了anaconda3无法使用conda

    ubuntu 中安装了 anaconda3 但是无法 使用 conda 只会出现这句话 conda 未找到指令 我找了一些办法 xff0c 有一个有用的 xff1a 8条消息 Ubuntu下使用Anaconda3 出现conda 未找到命令
  • ubuntu配置nfs时Failed to start nfs-server.service: Unit nfs-server.service not found.

    在ubuntu系统中配置nfs时出现Failed to start nfs server service Unit nfs server service not found 原因 xff1a 新装的ubuntu系统并未安装nfs 应使用su
  • 【经验分享】使用Keil5烧录代码遇到的问题及解决方法

    目录 一 前言 二 所遇问题及解决方法 1 首先最基本的Options for target 编辑的设置不用多说 xff0c 下载器根据自己所使用的类型进行选择 我使用的是CMSIS DAP 2 第二种可能出现的问题如下 SWD JTAG
  • c++ delete与析构函数的注意点

    问题 xff1a 我们都知道析构函数在类对象作用域结束时自动调用 xff0c 但这个规则适合基本类型 xff0c 但不适合delete函数 原因 xff1a 如果对象是new运算符动态创建的 xff0c 如果最后没有调用delete xff
  • 超详细!JAVA实现顺序表类

    Seqlist类 增 删 改 查 xff0c 判断是否为空 public class Seqlist lt T gt protected int n 顺序表元素个数 protected Object element 顺序表元素 public
  • 超详细!java实现链表

    Node lt T gt 结点类 public class Node lt T gt 结点类 数据域 xff1a data 存取域 xff1a next public T data 数据域 public Node lt T gt next
  • 超详细!java实现String部分方法

    java的String功能特点 Sring字符串是一个类 xff0c 属于引用数据类型 xff0c 提供比较大小 连接串等方法 String的对象是不是一个字符数组 xff0c 不能以数组下标格式s i 进行操作 xff0c 这和c c 4
  • 关于Java 的throw的一些注意的小点

    throw throw是程序中明确引发异常 xff0c 一旦执行到throw xff0c 程序就会被中断 xff0c 下面的代码就不会被执行 xff01 结论 xff1a 在编写代码阶段 xff0c 即使不运行程序 xff0c throw下
  • 栈究竟是什么?

    我们都知道 栈 这个数据结构 xff0c 它最大的特定就是 后进先出 xff0c 那么就会有一个问题 xff1f 真的存在天生就是 后进先出 的数据结构么 xff1f 答案是没有 xff01 结论 xff1a 栈的 后进先出 的规则是由人为
  • Maven的配置

    maven下载 首先登陆官网 点击download 然后点击下载 下载出来的是一个zip文件 直接解压到没有中文目录的文件夹下 我是放到java 中的 基础配置仓库的修改 打开apache maven gt 找到conf文件夹 gt 打开s
  • A - 简单密码(C语言)

    一 题目 Julius Caesar 曾经使用过一种很简单的密码 对于明文中的每个字符 xff0c 将它用它字母表中后 555 位对应的字符来代替 xff0c 这样就得到了密文 比如字符 A 用 F 来代替 如下是密文和明文中字符的对应关系
  • shell编程 -- 基础

    shell是一个命令行解释器 xff0c 它接收应用程序 用户命令 xff0c 然后调用操作系统内核 linux笔记 链接 xff1a https pan baidu com s 16GZCPfUTRzUqIyGnYwPuUg pwd 61
  • 专为折腾而生!老旧电脑安装PVE虚拟机保姆教程

    专为折腾而生 xff01 老旧电脑安装PVE虚拟机保姆教程 这几天玩VMware虚拟机上瘾 xff0c 感觉特别有意思 然而我其实并不满足于只是在这种软件层面上玩玩 xff0c 而想挑战更高级的玩法 xff0c 比如说玩玩可以安装在实体机上
  • idea中hdfs-api案例 :上传文件

    首先导入相关pom文件 lt dependencies gt lt hadoop相关依赖 gt lt dependency gt lt groupId gt org apache hadoop lt groupId gt lt artifa
  • 洛谷 P2078 朋友

    思路是分为两个并查集 xff0c 然后计算下男女人数 xff0c 然后直接比较 xff0c 选小的 代码写的有点麻烦好像 xff0c 交上去也没过 xff0c 虽然结果对了 其实第一遍已经发现有问题了 xff0c 因为比较的时候不小心把小于
  • 洛谷 P1359 租用游艇

    题目描述 长江游艇俱乐部在长江上设置了 nn 个游艇出租站 1 2 cdots n1 2 n 游客可在这些游艇出租站租用游艇 xff0c 并在下游的任何一个游艇出租站归还游艇 游艇出租站 ii 到游艇出租站 jj 之间的租金为 r i j
  • 寒假“最短路”题解

    1 P1359 租用游艇 题目描述 长江游艇俱乐部在长江上设置了 n 个游艇出租站 1 2 n 游客可在这些游艇出租站租用游艇 xff0c 并在下游的任何一个游艇出租站归还游艇 游艇出租站 ii 到游艇出租站 j 之间的租金为 r i j
  • 八数码问题

    I 问题介绍 八数码问题是一种经典的智力游戏 xff0c 也是一种搜索算法的经典案例 该问题是指在一个 3x3 的棋盘上 xff0c 有 8 个方块和一个空格 xff0c 每个方块上标有 1 8 的数字 xff0c 空格可以和相邻的方块交换