蓝桥python组模拟赛2(完结)

  1. 12.5MB

    【问题描述】
    在计算机存储中,12.5MB是多少字节?
    【答案提交】
    这是一道结果填空的题,你只需要算出结果后提交即可。本题的结果为一个整数,在提交答案时只填写这个整数,填写多余的内容将无法得分。



    1Byte=8Bit,它们之间存在着换算关系。bit中文叫「位/比特」,是计算机中存储数据的最小单位,它的值是0或1;Byte中文叫「字节」,是计算机文件大小的基本计算单位。

    1B(字节)=8bit(位)
    1KB=1024B
    1MB=1024KB
    1GB=1024M
    1TB=1024G

    res = 12.5 * 1024 * 1024
    print(res)
    

    13107200

  2. 最多边数

    【问题描述】
    一个包含有2019个结点的有向图,最多包含多少条边?(不允许有重边)
    【答案提交】
    这是一道结果填空的题,你只需要算出结果后提交即可。本题的结果为一个整数,在提交答案时只填写这个整数,填写多余的内容将无法得分。


    设结点数为n

    无向图边数:n * (n - 1) / 2

    有向图边数:n * (n - 1)

    res = 2019 * (2019 - 1)
    print(res)
    

    4074342

    问题描述
      一个包含有2019个结点的无向连通图,最少包含多少条边?
    答案提交
      这是一道结果填空的题,你只需要算出结果后提交即可。本题的结果为一个整数,在提交答案时只填写这个整数,填写多余的内容将无法得分。

    答案:2018
    解决办法:一个具有n个节点的无向连通图,其最少含有的边为n-1(其实就是这个图的生成树)。

  3. 单词重排

    【问题描述】
    将LANQIAO中的字母重新排列,可以得到不同的单词,如LANQIAO、AAILNOQ等,注意这7个字母都要被用上,单词不一定有具体的英文意义。
    请问,总共能排列如多少个不同的单词。
    【答案提交】
    这是一道结果填空的题,你只需要算出结果后提交即可。本题的结果为一个整数,在提交答案时只填写这个整数,填写多余的内容将无法得分。


    首先利用递归的思想去求所有的组合结果,然后利用**set()**集合去重。

    1. 先确定整个字符串的第一个字母是谁,对于长度为n的字符串吗,共有n种情况;
    2. 然后,问题就简化成从 返回的字符串中的字母的排列组合 变成了 返回第一个字母+除去第一个字母外的字符串的排列组合 ,类似于分治法的思想,解决一个大问题很难解决,我们就去解决它的子问题,利用子问题去解决大问题,其实就是递归思想。
    def perm(s=''):  # 默认参数为空字符串
        if len(s) <= 1:
            return s
    
        s1 = set()  # 集合去重
        
        for i in range(len(s)):
            for j in perm(s[0:i] + s[i+1:]):  # 去除一个字符剩下的串的排列组合
                s1.add(s[i] + j)  # 存储排列的字符串,集合特性自动去重
        
        return s1
    
    
    def main():
        s = 'LANQIAO'
        s1 = perm(s)
        # print(s1)
        print(len(s1))  # 2520
    
    if __name__ == '__main__':
        main()
    

    上边为python模块的正规写法,考试时建议不要这么干,直接写就行,这里吐槽一下IDLE,真难用,而且考试还要用!

    考试时这样写就行:

    def perm(s=''):  # 默认参数为空字符串
        if len(s) <= 1:
            return s
    
        s1 = set()  # 集合去重
    
        for i in range(len(s)):
            for j in perm(s[0:i] + s[i+1:]):  # 去除一个字符剩下的串的排列组合
                s1.add(s[i] + j)  # 存储排列的字符串,集合特性自动去重
    
        return s1
    
    print(len(perm('LANQIAO')))
    
  4. 括号序列

    【问题描述】
    由1对括号,可以组成一种合法括号序列:()。
    由2对括号,可以组成两种合法括号序列:()()、(())。
    由4对括号组成的合法括号序列一共有多少种?
    【答案提交】
    这是一道结果填空的题,你只需要算出结果后提交即可。本题的结果为一个整数,在提交答案时只填写这个整数,填写多余的内容将无法得分。


    首先就四对括号,那么手动暴力破解:

    1. 深度为1:()()()() 1
    2. 深度为2:(())(()) (())()() ()(())() ()()(()) (()())() ()(()()) (()()()) 7
    3. 深度为3:((()))() ()((())) ((()())) (()(())) ((())()) 5
    4. 深度为4:(((()))) 1

    一共 1 + 7 + 5 + 1 = 14 种

    其次代码当然也是可以解决的!

    def f(left, right):  # 利用递归解决
        
        global ans
        
        if left == 0 or right == 0:  # 无论左括号用完还是右括号匹配完就结束函数
            ans += 1  # 方法加1
            return
    
        f(left - 1, right)  # 先用一个左括号
        
        if left < right:  # 检测左括号数目如果小于右括号数目,则使用右括号
            f(left, right - 1)
    
    
    ans = 0
    
    
    f(4,4)  # 一共4对括号
    
    print(ans)
    
    
  5. 反倍数

    【问题描述】
    给定三个整数 a, b, c,如果一个整数既不是 a 的整数倍也不是 b 的整数倍还不是 c 的整数倍,则这个数称为反倍数。
    请问在 1 至 n 中有多少个反倍数。
    【输入格式】
    输入的第一行包含一个整数 n。
    第二行包含三个整数 a, b, c,相邻两个数之间用一个空格分隔。
    【输出格式】
    输出一行包含一个整数,表示答案。
    【样例输入】
    30
    2 3 6
    【样例输出】
    10
    【样例说明】
    以下这些数满足要求:1, 5, 7, 11, 13, 17, 19, 23, 25, 29。
    【评测用例规模与约定】
    对于 40% 的评测用例,1 <= n <= 10000。
    对于 80% 的评测用例,1 <= n <= 100000。
    对于所有评测用例,1 <= n <= 1000000,1 <= a <= n,1 <= b <= n,1 <= c <= n。


    很简单,这里只说一下处理输入数据时的问题。

    首先input()是输入数据,类型为字符串,我们先strip()一下去掉左右两边的空白,结果仍为一个字符串;然后再用split()分隔(默认是一空白分割),返回的是一个列表;之后再用map()对列表数据进行int格式化成整形,返回的是一个生成器格式数据;最后用list()一次性全部接收生成器中的所有元素,类型为列表。

    n = int(input())
    lst = list(map(int, input().strip().split()))
    
    count = 0
    # arr = []
    
    for i in range(1, n+1):
        if i % lst[0] !=0 and i % lst[1] != 0 and i % lst[2] != 0:
            count += 1
            arr.append(i)
    
    print(count)
    # print(arr)
    
  6. 凯撒加密

    【问题描述】
    给定一个单词,请使用凯撒密码将这个单词加密。
    凯撒密码是一种替换加密的技术,单词中的所有字母都在字母表上向后偏移3位后被替换成密文。即a变为d,b变为e,...,w变为z,x变为a,y变为b,z变为c。
    例如,lanqiao会变成odqtldr。
    【输入格式】
    输入一行,包含一个单词,单词中只包含小写英文字母。
    【输出格式】
    输出一行,表示加密后的密文。
    【样例输入】
    lanqiao
    【样例输出】
    odqtldr
    【评测用例规模与约定】
    对于所有评测用例,单词中的字母个数不超过100。


    s = input()
    res = ''
    
    for i in s:
        """加上 x y z 这三种特殊情况"""
        res += chr(((ord(i) - ord('a') + 3) % 26) + ord('a'))
    
    print(res)
    
    
  7. 螺旋

    【问题描述】
    对于一个 n 行 m 列的表格,我们可以使用螺旋的方式给表格依次填上正整数,我们称填好的表格为一个螺旋矩阵。
    例如,一个 4 行 5 列的螺旋矩阵如下:
    1 2 3 4 5
    14 15 16 17 6
    13 20 19 18 7
    12 11 10 9 8
    【输入格式】
    输入的第一行包含两个整数 n, m,分别表示螺旋矩阵的行数和列数。
    第二行包含两个整数 r, c,表示要求的行号和列号。
    【输出格式】
    输出一个整数,表示螺旋矩阵中第 r 行第 c 列的元素的值。
    【样例输入】
    4 5
    2 2
    【样例输出】
    15
    【评测用例规模与约定】
    对于 30% 的评测用例,2 <= n, m <= 20。
    对于 70% 的评测用例,2 <= n, m <= 100。
    对于所有评测用例,2 <= n, m <= 1000,1 <= r <= n,1 <= c <= m。


    def make_mat(x, y):
        global count
        while count <= m * n:
    
            while y < m and mat[x][y] == 0:  # y<m这个条件一定要放在mat[x][y]===0前边提前判断,否则,先判断是否值为0时会导致循环结束时的y值为m而报数组越界的错误
                mat[x][y] = count
                y += 1
                count += 1
    
            y -= 1  # 上个循环结束后,y的值为m,需要减一,否则越界
            x += 1  # 上一行读完后,行值加1
    
            while x < n and mat[x][y] == 0:  # 下边同理
                mat[x][y] = count
                x += 1
                count += 1
    
            x -= 1  # 上个循环结束后x值为n,要减一
            y -= 1  # 最后一列读完,往左推一列
    
            while y >= 0 and mat[x][y] == 0:
                mat[x][y] = count
                y -= 1
                count += 1
    
            y += 1  # 同理
            x -= 1
    
            while x >= 0 and mat[x][y] == 0:
                mat[x][y] = count
                x -= 1
                count += 1
    
            x += 1  # 同理
            y += 1
    
    
    n, m = list(map(int, input().split()))
    r, c = list(map(int, input().split()))
    
    count = 1
    
    mat = [[0] * m for i in range(n)]  # 注意二维数组的定义方法,不然会导致编一个位置其他行的相同位置也会随之改变
    
    make_mat(0, 0)
    
    # for line in mat:
        # print(*line)
    
    print(mat[r - 1][c - 1])
    
  8. 摆动序列

    【问题描述】
    如果一个序列的奇数项都比前一项大,偶数项都比前一项小,则称为一个摆动序列。即 a[2i]<a[2i-1], a[2i+1]>a[2i]。
    小明想知道,长度为 m,每个数都是 1 到 n 之间的正整数的摆动序列一共有多少个。
    【输入格式】
    输入一行包含两个整数 m,n。
    【输出格式】
    输出一个整数,表示答案。答案可能很大,请输出答案除以10000的余数。
    【样例输入】
    3 4
    【样例输出】
    14
    【样例说明】
    以下是符合要求的摆动序列:
    2 1 2
    2 1 3
    2 1 4
    3 1 2
    3 1 3
    3 1 4
    3 2 3
    3 2 4
    4 1 2
    4 1 3
    4 1 4
    4 2 3
    4 2 4
    4 3 4
    【评测用例规模与约定】
    对于 20% 的评测用例,1 <= n, m <= 5;
    对于 50% 的评测用例,1 <= n, m <= 10;
    对于 80% 的评测用例,1 <= n, m <= 100;
    对于所有评测用例,1 <= n, m <= 1000。


    具体详解可参考https://blog.csdn.net/the_ZED/article/details/106029631这篇的博主是用c写的,我用python写的代码在下边。

    这个题用普通的搜索肯定很难解决,而且造成递归树过大。这时候我们通常可以用记忆化搜索或动态规划来解决。首先,动态规划解决的话我们首先要做的就是寻找状态转移方程。有两个关键的地方:

    1. 位置的奇偶会影响当前位置上的取值规则;

      1. 所取值必须停在区间[1,n]中。

    对于第一点的规则就是奇数项都比前一项大,偶数项都比前一项小,这个我们可以通过用一个二维状态(dp[i][j])来进行标记。比如对于dp[i][j]=x,其具体的逻辑意义是:在第i个位置上选择数字j时共有x个方案。怎么来得到dp[i][j]呢?这显然和i的奇偶性有关:

    1. 当i是奇数时,由于其比前面的每一项都大,因此对于dp[i][j]而言,其前面可以存在的合法项就有:dp[i-1][1]、dp[i-1][2]、dp[i-1][3]、……、dp[i-1][j-1]。换言之dp[i][j]=dp[i-1][1]+dp[i-1][2]+dp[i-1][3]+……+dp[i-1][j-1];
    2. 当i是偶数时,由于其比前面的每一项都小,因此对于dp[i][j]而言,其前面可以存在的合法项就有:dp[i-1][j+1]、dp[i-1][j+2]、dp[i-1][j+3]、……、dp[i-1][n]。换言之dp[i][j]=dp[i-1][j+1]+dp[i-1][j+2]+dp[i-1][j+3]+……+dp[i-1][n];

    于是我们就得到了本题的状态转移方程为:

    img

    最后还需要对dp数组进行初始化。试想,对于序列的首项而言,其并不存在所谓的“前一项”,因此在首项处可以任意取值,当然,其每个取值都只有一种方案。即:dp[1][1]=1,dp[1][2]=1,dp[1][3]=1,……,dp[1][n]=1
    求出dp数组后怎么使用呢?要知道dp[i][j]的逻辑意义是在第i个位置上选择数字j时的方案数,那么在长度为m,取值范围为[1,n]的前提下,总的摆动序列的数量就可以用dp[m][1]+dp[m][2]+dp[m][3]+……+dp[m][n]来表示。

    下面直接给出基于上述思路写出的完整代码(看不太懂的减一把程序copy进pycharm然后进行debug观察变量的变化情况,非常直观,不会的可以留言,我找时间会做个详细教程):

    def dp(m, n):
        ans = 0
        temp = 0
        for i in range(1, n+1):
            lst[1][i] = 1  # 初始化数组
        for i in range(2, m+1):
            for j in range(1, n+1):
                temp = 0
                if i % 2 != 0:  # 奇数
                    for k in range(1, j):
                        temp = (temp + lst[i-1][k]) % MOD
                else:  # 偶数
                    for k in range(j+1, n+1):
                        temp = (temp + lst[i-1][k]) % MOD
                lst[i][j] = temp
        for i in range(1, n+1):
            ans = (ans + lst[m][i]) % MOD
    
        return ans
    
    
    m, n = list(map(int, input().split()))
    MOD = 10000
    lst = [[0] * (n+2) for i in range(m+2)]
    
    print(dp(m, n))
    

    这个代码很容易理解,但是问题也很突出,那就是在DP时,我们用到了3层循环!!!(更严格的说,其时间复杂度为O(n3))这在m=n=1000的极限情况下超时无疑,因此我们不得不进行优化。

    在上面之所以用了3层循环,是因为dp[i][j]这个状态表示的是其在第i个位置上选择数字j时的方案数,这是一个“1对1”的存储模式。它导致程序在后面进行状态转移时,需要对每一个dp[x][y]进行更新维护,而更新维护的时候又是从前一次的{ dp[i-1][1],dp[i-1][2],……,dp[i-1][n] }中寻找。遍历dp[x][y]是一个二重循环,更新维护又是一重循环,因此上面的程序是一个三重循环。

    如果我们试图降低本程序的时间复杂度,就不得不从上面的短板中寻找突破。
    一个比较直观,且容易想到的改进是:能不能对dp[i][j]这个存储模式进行更改?如果我们直接将dp[i][j]的存储模式改为“1对多”,那就相当于降低了一重循环。
    现在我们就来着手实现对dp[i][j]的模式更改。由于位置i的奇偶性会影响当前位置上的取值规则,因此我们也可以将dp[i][j]的意义根据位置的奇偶性进行如下重定义:

    1. 当i为奇数时,dp[i][j]表示第i个数选择大于等于数j的方案数;
    2. 当i为偶数时,dp[i][j]表示第i个数选择小于等于数j的方案数。

    这样的改变有点类似于将奇数位上的数用后缀数组来表达,而偶数位上的数用前缀数组来表达。那么此时我们的动态转移方程也会发生相应的改变,如下:

    1. 当i为奇数时,由于奇数项都比前一项大,那么其前一项只能是dp[i-1][j-1]。又由于此时dp[i][j]表示的是“第i个数选择大于等于数j的方案数”,那么我们还需要额外加上当前项的后一项,即dp[i][j+1]。因此,此时dp[i][j]=dp[i-1][j-1]+dp[i][j+1]
    2. 当i为偶数时,由于偶数项都比前一项小,那么其前一项只能是dp[i-1][j+1]。又由于此时dp[i][j]表示的是“第i个数选择小于等于数j的方案数”,那么我们还需要额外加上当前项的前一项,即dp[i][j-1]。因此,此时dp[i][j]=dp[i-1][j+1]+dp[i][j-1]。

    这样一来,我们就省去了上面那个算法的最内层循环,从而将时间复杂度降低到O(n2)。即使在极限情况下,改进后的算法也能从容应对。

    接下来是初始化问题,我们在将dp[i][j]存储的信息进行更改后,其意义是一个累加值(随当前位置i的奇偶性而决定累加方向)。由于首项dp[1][y]所在位置1是一个奇数,那么其累加方向是自右向左,即:dp[1][1]=n,dp[1][2]=n-1,dp[1][3]=n-2,……,dp[1][n-1]=2,dp[1][n]=1。
    现在求出dp数组后又怎么使用呢?还是从dp[i][j]的逻辑意义出发:“当i为奇数时,dp[i][j]表示第i个数选择大于等于数j的方案数;当i为偶数时,dp[i][j]表示第i个数选择小于等于数j的方案数”。显然,这时候我们的结果是依赖于序列长度的奇偶性:
    如果序列长度为奇数,那为了能将所有的结果都包含进来,就应该用dp[m][1]来表示该波动序列的总量;
    如果序列长度为偶数,那为了能将所有的结果都包含进来,就应该用dp[m][n]来表示该波动序列的总量;

    下面给出改进后,本题的完整代码:

    def dp(m, n):
        for i in range(1, n+1):
            lst[1][i] = n - i + 1  # 初始化数组
        for i in range(2, m+1):
            if i % 2 != 0:  # 奇数
                for j in range(n, 0, -1):
                    lst[i][j] = (lst[i-1][j-1] + lst[i][j+1]) % MOD
            else:  # 偶数
                for j in range(1, n+1):
                    lst[i][j] = (lst[i-1][j+1] + lst[i][j-1]) % MOD
    
        return lst[m][1] if m % 2 != 0 else lst[m][n]
    
    
    m, n = list(map(int, input().split()))
    MOD = 10000
    lst = [[0] * (n+2) for i in range(m+2)]
    
    print(dp(m, n))
    
  9. 通电

    【问题描述】
    2015年,全中国实现了户户通电。作为一名电力建设者,小明正在帮助一带一路上的国家通电。
    这一次,小明要帮助 n 个村庄通电,其中 1 号村庄正好可以建立一个发电站,所发的电足够所有村庄使用。
    现在,这 n 个村庄之间都没有电线相连,小明主要要做的是架设电线连接这些村庄,使得所有村庄都直接或间接的与发电站相通。
    小明测量了所有村庄的位置(坐标)和高度,如果要连接两个村庄,小明需要花费两个村庄之间的坐标距离加上高度差的平方,形式化描述为坐标为 (x_1, y_1) 高度为 h_1 的村庄与坐标为 (x_2, y_2) 高度为 h_2 的村庄之间连接的费用为
    sqrt((x_1-x_2)*(x_1-x_2)+(y_1-y_2)*(y_1-y_2))+(h_1-h_2)*(h_1-h_2)。
    在上式中 sqrt 表示取括号内的平方根。请注意括号的位置,高度的计算方式与横纵坐标的计算方式不同。
    由于经费有限,请帮助小明计算他至少要花费多少费用才能使这 n 个村庄都通电。
    【输入格式】
    输入的第一行包含一个整数 n ,表示村庄的数量。
    接下来 n 行,每个三个整数 x, y, h,分别表示一个村庄的横、纵坐标和高度,其中第一个村庄可以建立发电站。
    【输出格式】
    输出一行,包含一个实数,四舍五入保留 2 位小数,表示答案。
    【样例输入】
    4
    1 1 3
    9 9 7
    8 8 6
    4 5 4
    【样例输出】
    17.41
    【评测用例规模与约定】
    对于 30% 的评测用例,1 <= n <= 10;
    对于 60% 的评测用例,1 <= n <= 100;
    对于所有评测用例,1 <= n <= 1000,0 <= x, y, h <= 10000。


    可以先学习下python版最小生成树Prim和Kruskal算法

    方法一prim的两种解法:

    """
    解法1
    Prim类接受两个参数n,m,分别表示录入的点和边的个数(点的编号从1开始递增)
    接下来是m行,每行3个数x,y,length,表示点x和点y之间存在一条长度为length的边 
    程序最终会打印出该无向图中的最小生成树的各个边和最小权值 
    """
    import math
    
    class Node:
        """
        Node类存放节点信息,
        node是与某个节点(实际上是用列表索引值代表的)相连接的点
        length表示这两个点之间的权值
        """
        def __init__(self, x, y, h):
            self.x = x
            self.y = y
            self.h = h
    
    
    class Edge:
        """
        Edge类表示边的信息
        x,y表示这条边的两个顶点
        length为<x,y>之间的距离
        """
        def __init__(self, x, y, cost):
            self.x = x
            self.y = y
            self.cost = cost
    
    
    class Prim:
        """Prim算法:求解无向连通图中的最小生成树 """
        def __init__(self, n, adj_map):
            self.n = n  # 输入的点个数
            self.adj_map = adj_map
            self.cost = 0
            self.node = [0 for i in range(n + 1)]  # 存放所有节点之间的可达关系与距离
            """图的邻接表的表示法,v是一个二维列表,其索引值代表当前节点,索引值对应的一维列表中存放的
            是与以当前索引值为顶点的相连的节点信息"""
            self.e = []  # 存放与当前已选节点相连的边
            self.vis = [False for i in range(n+1)]  # 标记每个点是否被访问过,False未访问
    
        def create(self):
            for i in range(1, self.n):
                for j in range(i+1, self.n+1):
                    self.adj_map[i][j] = self.adj_map[j][i] = math.sqrt((self.node[i].x - self.node[j].x) ** 2 + (self.node[i].y - self.node[j].y) ** 2) + (self.node[i].h - self.node[j].h) ** 2
    
        def graphy(self):
            """构建图,这里用的是邻接表"""
            for i in range(1, self.n+1):
                x, y, h = list(map(int, input().split()))
                self.node[i] = Node(x, y, h)
            self.create()
    
        def insert(self, point):
            self.vis[point] = True
            for i in range(1, self.n+1):
                if not self.vis[i]:
                    self.e.append(Edge(point, i, self.adj_map[point][i]))
            self.e = sorted(self.e, key=lambda e: e.cost)  # 把e中的所有边按边的长度从小到大排序
            # for i in self.e:
            #     print(i.cost, i.x, i.y)
    
        def run(self, start):
            """执行函数:求解录入的无向连通图的最小生成树"""
            self.insert(start)  # start为选择的开始顶点
            selected = 1
            # exit()
            while selected < self.n:
                for i in range(len(self.e)):
                    if not self.vis[self.e[i].y]:
                        self.cost += self.e[i].cost
                        self.insert(self.e[i].y)
                        selected += 1
                        break
            return self.cost
    
    
    def main():
        n = int(input())
        adj_map = [[0] * (n+1) for i in range(n+1)]
        prim = Prim(n, adj_map)
        prim.graphy()
        # print(adj_map)
        cost = prim.run(1)
        print(round(cost, 2))
    
    
    if __name__ == '__main__':
        main()
    
    
    import sys
    import math
    MAX = sys.maxsize
    
    
    class Prim:
        '''
            prim算法求最小生成树
            :param graph: 图
            :return: 最小的权值
            :n:点的个数
            '''
        def __init__(self, n, graphy):
            self.n = n
            self.cost = 0
            self.v = 0
            self.graphy = graphy
            self.lowcost = []  #记录当前顶点集合到剩下的点的最低权值,“-1”表示已访问过的点,无需访问
            self.mst = []  ##记录当前被更新的最低权值来自于哪个点,相当于记录是边的起始点,lowcost的下标表示的是最低权值的终止点
    
        def run(self):
            for i in range(self.n):
                self.lowcost.append(self.graphy[0][i])
                self.mst.append(0)
            self.lowcost[0] = -1
            for i in range(self.n-1):
                min = MAX
                for j in range(self.n):
                    if min > self.lowcost[j] and self.lowcost[j] != -1:
                        min = self.lowcost[j]
                        self.v = j
                self.cost += min
                # print(f'{self.mst[self.v]}->{self.v}:{min}')
                self.lowcost[self.v] = -1
                for k in range(self.n):
                    if self.lowcost[k] > self.graphy[self.v][k]:
                        self.lowcost[k] = self.graphy[self.v][k]
                        self.mst[k] = self.v
            return self.cost
    
    
    def main():
        n = int(input())
        data = [list(map(int, input().split())) for i in range(n)]
        graphy = [[MAX] * n for i in range(n)]
        for i in range(n-1):
            for j in range(i + 1, n):
                graphy[j][i] = graphy[i][j] = math.sqrt((data[i][0] - data[j][0]) ** 2 + (data[i][1] - data[j][1]) ** 2) + (data[i][2] - data[j][2]) ** 2
        # print(data)
        # print(graphy)
        prim = Prim(n, graphy)
        # cost = prim.run()
        cost = round(prim.run(),2)
        print(cost)
    
    
    if __name__ == '__main__':
        main()
    

    方法二Krukal方法:

    import math
    class Edge:
        """
        Edge类表示边的信息
        x,y表示这条边的两个顶点
        length为<x,y>之间的距离
        """
        def __init__(self, x, y, cost):
            self.x = x
            self.y = y
            self.cost = cost
    
    class UnionFindSet:
        """并查集类,用于连通两个节点以及判断图中的所有节点是否连通 """
        def __init__(self, start, n):
            self.start = start  # start和n分别用于指示并查集里节点的起点和终点
            self.n = n
            self.pre = [0 for i in range(self.n - self.start + 2)]  # pre数组用于存放某个节点的上级
            self.rank = [0 for i in range(self.n - self.start + 2)]  # rank数组用于降低关系树的高度
    
        def init(self):
            """初始化并查集"""
            for i in range(self.start, self.n+1):
                self.pre[i] = i
                self.rank[i] = 1
    
        def find_pre(self, x):
            """寻找节点x的上一级节点"""
            if self.pre[x] == x:
                return x
            else:
                self.pre[x] = self.find_pre(self.pre[x])
            return self.pre[x]
    
        def is_same(self, x, y):
            """判断x节点和y节点是否连通"""
            return self.find_pre(x) == self.find_pre(y)
    
        def unite(self, x, y):
            """判断两个节点是否连通,如果未连通则将其连通并返回真,否则返回假"""
            x = self.find_pre(x)
            y = self.find_pre(y)
            if x == y:
                return False
            if self.rank[x] > self.rank[y]:
                """类似于平衡二叉树的概念"""
                self.pre[y] = x
            else:
                if self.rank[x] == self.rank[y]:
                    self.rank[y] += 1
                self.pre[x] = y
            return True
    
        def is_one(self):
            """判断整个无向图中的所有节点是否连通 """
            temp = self.find_pre(self.start)
            for i in range(self.start+1, self.n+1):
                if self.find_pre(i) != temp:
                    return False
            return True
    
    
    class Kruskal:
        """Kruskal算法:求解无向连通图中的最小生成树 """
        def __init__(self, n, m):
            self.n = n  # n,m分别表示输入的点和边的个数
            self.m = m
            self.cost = 0
            self.node = [0 for i in range(n + 1)]  # 存放所有节点之间的可达关系与距离
            self.e = []  # 存放录入的无向连通图的所有边
            self.s = []  # 存放最小生成树里的所有边
            self.u = UnionFindSet(1, self.n)  # 并查集:抽象实现Graphnew,并完成节点间的连接工作以及判断整个图是否连通
    
        def create(self):
            # print(self.node)
            for i in range(1, self.n):
                for j in range(i+1, self.n+1):
                    self.e.append(Edge(i, j, math.sqrt((self.node[i].x - self.node[j].x) ** 2 + (self.node[i].y - self.node[j].y) ** 2) + (self.node[i].cost - self.node[j].cost) ** 2))
    
        def graphy(self):
            """这里只是存储所有边的信息并按边的长度排序"""
            for i in range(1, self.n+1):
                x, y, h = list(map(int, input().split()))
                self.node[i] = Edge(x, y, h)
            self.create()
            self.e.sort(key=lambda e: e.cost)
            # for i in self.e:
            #     print(i.h)
            self.u.init()
    
        def run(self):
            """执行函数:求解录入的无向连通图的最小生成树 """
            for i in range(self.m):
                if self.u.unite(self.e[i].x, self.e[i].y):
                    self.s.append(self.e[i])
                if self.u.is_one():
                    """一旦Graphnew的连通分支数为1,则说明求出了最小生成树 """
                    break
    
        def print(self):
            print(f'构成最小生成树的边为:')
            edge_sum = 0
            for i in range(len(self.s)):
                print(f'边 <{self.s[i].x},{self.s[i].y}> = {self.s[i].cost}')
                edge_sum += self.s[i].cost
            print(f'最小生成树的权值为:{edge_sum}')
    
    def main():
        n = int(input())
        m = int((n * (n - 1)) / 2)
        kruskal = Kruskal(n, m)
        kruskal.graphy()
        kruskal.run()
        kruskal.print()
    
    if __name__ == '__main__':
        main()
    

    运行结果(输出自己改一下):

    4
    1 1 3
    9 9 7
    8 8 6
    4 5 4
    构成最小生成树的边为:
    边 <2,3> = 2.414213562373095
    边 <1,4> = 6.0
    边 <3,4> = 9.0
    最小生成树的权值为:17.414213562373096
    
  10. 植树

    【问题描述】
    小明和朋友们一起去郊外植树,他们带了一些在自己实验室精心研究出的小树苗。
    小明和朋友们一共有 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
    【评测用例规模与约定】
    对于 30% 的评测用例,1 <= n <= 10;
    对于 60% 的评测用例,1 <= n <= 20;
    对于所有评测用例,1 <= n <= 30,0 <= x, y <= 1000,1 <= r <= 1000。


    考点:两圆之间的位置关系

    首先建议先做一下这个题:判断两个圆之间的关系

    下边是本题代码:

    import math
    
    
    class Circle:
        """存储每颗树的位置信息,和它的覆盖面积,并提供判断自身与另一颗树的覆盖范围的位置关系"""
        def __init__(self, x, y, r):
            self.x = x
            self.y = y
            self.r = r
            self.absolute_area = r ** 2
    
        def judge_relation(self, *args):
            """
            判断两圆之间的关系
            @args:多值参数
            """
            if args:  # args(这里的args就是一个元组)不为空时
                flag = [False for i in range(len(args))]
                i = 0
                for circle in args:
                    dis = math.sqrt((self.x - circle.x) ** 2 + (self.y - circle.y) ** 2)
                    if self.r + circle.r < dis or self.r + circle.r == dis:  # 外离和外切
                        """若两个圆属于外切和外离的情况,置flag[i]为真"""
                        flag[i] = True
                    i += 1
                return flag
            else:
                # args为空,第一个圆,肯定不会存在与其他圆的位置关系情况,直接返回一个元组,因为后边的all()的参数必须为可迭代对象
                return [True, ]
    
    
    class PlantTree:
        def __init__(self, n):
            self.n = n  # 待植的树的棵数
            self.tree = [0 for i in range(n)]  # 存放每一颗树的位置信息
            self.vis = []  # 存放已经种植的树
            self.total_area = 0  # 已经种植的树覆盖的总面积
    
        def init(self):
            for i in range(self.n):
                x, y, r = list(map(int, input().split()))
                self.tree[i] = Circle(x, y, r)
            self.tree.sort(key=lambda circle: circle.r, reverse=True)  # 半径从大到小排序,覆盖面积最大的先种
    
        def plant(self):
            for i in range(self.n):
                # flag = all(self.tree[i].judge_relation(*self.vis))
                if all(self.tree[i].judge_relation(*self.vis)):
                    """注意all()的参数必须是可迭代对象,vis是一个列表用*打散传入"""
                    # 如果当前树与已种的树不会相交,则把此树放入已种树的列表。
                    self.vis.append(self.tree[i])
                    self.total_area += self.tree[i].absolute_area
            return self.total_area
    
    
    def main():
        n = int(input())
        plant_tree = PlantTree(n)
        plant_tree.init()
        plant_tree.plant()
        print(plant_tree.total_area)
    
    
    if __name__ == '__main__':
        main()
    
    

猜你喜欢

转载自blog.csdn.net/qq_31910669/article/details/108679544