蓝桥杯种树(递归)

1. 问题描述:

小明和朋友们一起去郊外植树,他们带了一些在自己实验室精心研究出的小树苗。
小明和朋友们一共有 n 个人,他们经过精心挑选,在一块空地上每个人挑选了一个适合植树的位置,总共 n 个。他们准备把自己带的树苗都植下去。
然而,他们遇到了一个困难:有的树苗比较大,而有的位置挨太近,导致两棵树植下去后会撞在一起。他们将树看成一个圆,圆心在他们找的位置上。如果两棵树对应的圆相交,这两棵树就不适合同时植下(相切不受影响),称为两棵树冲突。小明和朋友们决定先合计合计,只将其中的一部分树植下去,保证没有互相冲突的树。他们同时希望这些树所能覆盖的面积和(圆面积和)最大。

输入

输入的第一行包含一个整数 n ,表示人数,即准备植树的位置数。
接下来 n 行,每行三个整数 x, y, r,表示一棵树在空地上的横、纵坐标和半径。

输出

输出一行包含一个整数,表示在不冲突下可以植树的面积和。由于每棵树的面积都是圆周率的整数倍,请输出答案除以圆周率后的值(应当是一个整数)。
样例
输入
6
1 1 2
1 4 2
1 7 2
4 1 2
4 4 2
4 7 2

输出:12

2. 思路分析:

① 分析题目可以知道,我们需要尝试所有可能的种植方案,找出在不冲突的情况下可以种的最大面积和,因为需要尝试可能的种植方案,根据这个特点我们可以使用递归解决,递归可以找出所有不冲突的种植方案,然后在递归的过程中找出可以种植的最大面积和。因为涉及到两个点对应的圆是否相交可以使用公式:(x1 - x2) ^ 2 + (y1 - y2)^ 2 > (r1 + r2) ^ 2进行判断,如果成立说明是不相交的,不成立说明是相交的,其中x1,y1,x2,y2分别表示两个点的坐标,r1,r2表示对应位置的半径。为了在递归的过程中避免重复计算两个点对应的圆是否相交,可以对输入数据进行预处理计算两个位置对应的圆是否相交,将结果存储到二维列表中,这样就可以通过下标关系在二维列表中直接判断两个圆是否相交。递归的思路其实也比较好想到,可以这样想:目前我有n个位置,尝试在第index的位置上种树,需要判断index位置之前的已经种的树是否与当前尝试种的树发生冲突,如果发生了冲突那么就不种当前的树,可以使用一个vis列表来记录下目前已经种的树对应的位置,这样就可以通过vis数组判断当前尝试种的树是否与之前的树发生冲突,只有当我种树的时候不发生冲突的情况下我才将vis[i] = 1

② 其实这个有点类似于全排列的生成,在当前的位置可以选择种也可以选择不种,只是题目中有个限制条件就是种的树相互之间不能够发生冲突所以在尝试种当前树的时候需要检验一下是否可以种下。可以选择种也可以选择不种,对应着两种平行状态,两种平行状态的递归对应两种写法,第一种是直接在方法体中写两个递归方法表示选择与不选择,第二种是在for循环中进行递归,for循环的范围为当前的index到n(n为位置的数目),每一次往下递归的时候都是index + 1,本质上表示的意思也还是选择与不选择,当循环到i位置的时候表示选择i位置,当递归返回到当前这一层尝试下一个位置的时候那么表示的就是不选择当前的位置

③ 因为没有测试网站,所以也不知道结果是否完全正确,当数据量大的时候可能会超时吧

3. 代码如下:

for循环中递归:

import sys
from typing import List

res = 0


def dfs(isIntersect: List[List[bool]], vis: List[int], index: int):
    # 使用global关键字可以修改全局变量
    global res
    if index == len(vis):
        area = 0
        for i in range(len(vis)):
            if vis[i]: area += (points[i][2]) ** 2
        # print(vis)
        res = max(res, area)
        return
    # 在for循环中进行递归, 尝试种当前的第i棵树
    for i in range(index, len(vis)):
        f = 1
        for j in range(i):
            # 通过vis列表判断当前尝试种的树是否与之前的发生冲突
            if vis[j] and not isIntersect[i][j]:
                f = 0
                break
        # 确保可以种的情况下那么种下这棵树
        if f: vis[i] = 1
        dfs(isIntersect, vis, i + 1)
        # 回溯, 尝试下一个可能的种的位置
        vis[i] = 0


if __name__ == '__main__':
    # 将数据封装在列表中会更方便操作
    n = int(input())
    points = list()
    for i in range(n):
        points.append(list(map(int, input().split())))
    # 计算两个点对应的圆是否相交
    isIntersect = [[False] * n for i in range(n)]
    # 预处理两个位置对应的圆是否相交
    for i in range(n - 1):
        for j in range(i + 1, n):
            # 判断是否相交
            t = (points[i][0] - points[j][0]) ** 2 + (points[i][1] - points[j][1]) ** 2 > (
                    points[i][2] + points[j][2]) ** 2
            isIntersect[i][j] = isIntersect[j][i] = t
    vis = [0] * n
    dfs(isIntersect, vis, 0)
    print(res)

 

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值