营救问题(python实现)

营救 (通信网理论基础)

题目大意:

你是红军指挥官,在一场军事演习中,你的部分军队被蓝军包围了。蓝军包围的方式如下
在这里插入图片描述
在上图中,每个顶点表示蓝军的部队,顶点中数字表示蓝军在此处的人数(千人),两点间的边表示蓝军两个部队间形成的火线,火线构成的圈即是一道包围,一条火线的战斗力为其相连两个部队的人数和,也是你要进攻这条火线所要消耗的兵力。你可以同时进攻蓝军的多条火线,请以成本最低的方式打破蓝军包围,营救被包围的部队,计算出所需要消耗的兵力数(千人)
在这里插入图片描述
输入:
输入包含多个测试例。
第一行是一个整数n,表示测试测试例数量。
对每个测试例:
第一行是一个正整数m(2<m200),是蓝军部队数。
每个蓝军部队有两行:
一行3个正整数:i为部队编号(0im-1),ai为蓝军部队人数(千人)(1ai100),bi为与该部队能形成火线的部队数量(1bim-1)。
一行有bi个正整数,分别是与部队i形成火线的部队编号,数字间有空格。
至少有一个包围。
输出:
每个测试例输出一行,是一个正整数:打破包围营救战友的最小消耗兵力。
输入样例:
1
3
0 1 2
1 2
1 2 2
0 2
2 3 2
0 1
在这里插入图片描述
输出结果:
3

题目分析:形成一个最大生成树,剃掉的边即为所需破圈边。

import sys, time
from heapq import heappop, heappush

edgeLinks = dict()
edgeWeights = []
edgeWeightsAbout = dict()
P = dict()
nodes = set()
count=0
sum=0

class UnionFind():  #数据结构建立
    def __init__(self, nodes):
        self.prior = {}
        self.setNum = {}
        for node in nodes:
            self.prior[node] = node
            self.setNum[node] = 1
    def findP(self, node):   #
        prior = self.prior[node]
        if (node != prior):
            prior = self.findP(prior)
        self.prior[node] = prior
        return prior

    def union(self, left, right): #维护功能
        if left is None or right is None: return
        aP = self.findP(left)
        bP = self.findP(right)
        if (aP != bP):
            aNum = self.setNum[aP]
            bNum = self.setNum[bP]
            if (aNum <= bNum):
                self.prior[aP] = bP
                self.setNum[bP] = aNum + bNum
                self.setNum.pop(aP)
            else:
                self.prior[bP] = aP
                self.setNum[aP] = aNum + bNum
                self.setNum.pop(bP)

    def isSameSet(self, left, right):  # 判断left和right两个集合是否相同
        return self.findP(left) == self.findP(right)



def kruskalMST():
    global edgeLinks, edgeWeights,count
    forest = UnionFind(nodes)
    count=0
    while edgeWeights != []:
        minWeight = heappop(edgeWeights)#把最小权重边弹出
        if minWeight in edgeWeightsAbout.keys(): #判断是否存在该集合
            minEdge = edgeWeightsAbout[minWeight].pop() #最小边弹出
            a = minEdge.pop()#前后节点弹出
            b = minEdge.pop()
            if edgeWeightsAbout[minWeight] == []:
                edgeWeightsAbout.pop(minWeight)
            if not forest.isSameSet(a,b):
                print("剩%c->%c"%(a,b))#拓展顺序打印
                count=count-minWeight
                forest.union(a,b)#调用union函数

    # print(count)


t=int(input())
# print(type(t))
for item in range(t):
    V = int(input())
    if (V < 2 or V > 200):
        sys.exit()
    for item1 in range(V):
        a, b, c = map(int, input().split())
        nodes.add(str(a))
        bi = int(b)
        edge = []
        edge = input().split()
        # print(edge)
        P.update({a: bi})
        # print(P[0])
        for item2 in range(c):
            if str(a) not in edgeLinks:edgeLinks[str(a)] = set()
            edgeLinks[str(a)].add(str(edge[item2]))
            if a>int(edge[item2]):
                if P[int(edge[item2])] != None:
                    t=P[a] + P[int(edge[item2])]
                    sum=sum+t
                    z=-t
                    if z not in edgeWeightsAbout.keys():
                        edgeWeightsAbout[z] = []
                    if {str(a), edge[item2]} not in edgeWeightsAbout[z]:
                        edgeWeightsAbout[z].append({str(a), edge[item2]})
                    # # edgeWeights.append(t)
                    if z not in edgeWeightsAbout.keys():edgeWeightsAbout[z] = []
                    if {str(a), str(edge[item2])} not in edgeWeightsAbout[z]:edgeWeightsAbout[z].append({str(a), str(edge[item2])})
                    heappush(edgeWeights, z)
                    # print(edgeWeights)
                    # print(edgeLinks)
                    # print(P)
                    # print(edgeLinks)
                    # print(edgeWeights)
                    # print(edgeWeightsAbout)
    kruskalMST()
    print(sum-count)
    count=0
    sum=0
    

输入数据和结果如下:
在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值