2021华为软挑(区域初赛)总结
2021华为软挑(区域初赛)总结
文章目录
- 2021华为软挑(区域初赛)总结
- 一、案例解析
- 输入
- 输出
- 二、输入输出详解
- 输入详解
- 输出详解
- 三、易错点提前解析
- 1.Q的值
- 2. 服务器id问题
- 3. 迁移问题
- 四、思路分析
- 五、参数声明
- 六、代码实现
- 1. 服务器性价比代码
- 2. 最佳服务器的获取
- 3. 删除虚拟机
- 4. 执行添加虚拟机算法
- 5. 整合服务器算法
- 6. 设置虚拟机
- 7. 迁移算法
- 8. 获取服务器与虚拟机算法
- 七、总代码
一、案例解析
输入
2 # 一共两种服务器
(NV603, 92, 324, 53800, 500)
(NV604, 128, 512, 87800, 800)
2 # 一共两种虚拟机
(c3.large.4, 2, 8, 0)
(c3.8xlarge.2, 32, 64, 1)
3 # 一共3天
2 # 第一天一共两种操作
(add, c3.large.4, 5)
(add, c3.large.4, 0)
2 # 第二天一共2种草祖宗
(del, 0)
(add, c3.8xlarge.2, 1)
3 # 第三天一共3种操作
(add, c3.large.4, 2)
(del, 1)
(del, 2)
输出
(purchase, 2) # 第一天购买两台服务器
(NV603, 1) # 编号为0,NV603
(NV604, 1) # 编号为1,NV604
(migration, 0) # 迁移0次
(0, A) # 更具第一天的输入操作,表示id为5的虚拟机部署在第0台服务器的A节点
(0, B) # id为0的虚拟机部署在第0太服务器的B节点
(purchase, 0) # 第二天购买0台服务器
(migration, 0) # 迁移0次
(1) # 更具第二天输入的值,表示id为1的虚拟机部署在第1台服务器上(双节点,不需要配置节点)
(purchase, 0) # 第三天,购买0台服务器
(migration, 0) # 迁移0次
(1, B) # 更具第三天输入的值,表示id为2的虚拟机部署在第1台服务器的B节点
需要注意:双节点配置的时候,由于负载均衡,因此节点A和B同时分担服务器的内存和cpu
二、输入输出详解
输入详解
- 第一个数N:表示一共N种服务器
- 接下来N行为所有的服务器
- 说明:
(型号,CPU核数、内存大小、硬件成本,每日能耗成本)
- 存储格式:
型号:CPU核数、内存大小、硬件成本,每日能耗成本
- 第二个数M:表示M种虚拟机
- 接下来M行为所有的虚拟机
- 说明:
(型号、CPU核数、内存大小、是否双节点部署)
- 存储格式:
型号:[CPU核数、内存大小、是否双节点部署]
- 注:0为单节点、1为双节点
- 第三个数T:表示一共有T天
- 接下来T*a行表示T天的请求
- 第四个数S:表示该天有S个请求
- 接下来S行为每天的请求
- 说明:
(add,虚拟机型号,虚拟机ID)
(del,虚拟机ID)
输出详解
- 总共T*a行
- 没以块a表示改天的购买、迁移与添加操作
- a块详解
- 第一个数:
(purchase, Q)
- Q为当天新购买Q太服务器
- 接下来Q行表示购买的服务器类型与个数
- 例:
(NV603, 2)
,表示购买2台NV603型号服务器- 此时,第一台NV603id为0
- 第二台id为1
- 如果有新的服务器购买,则id为3(不管是不是这个类型)
- 第二个数:
(migration, W)
- W为当天迁移的虚拟机数量:W必须小于当前虚拟机总数的5*n/1000
- 接下来W行,每一行表示一个虚拟机的迁移
- 例1:
(虚拟机ID,目的服务器ID)
- (3, 1)表示将 ID 为 3 的虚拟机从当前所在服务器迁移至 ID 为 1 的服务器,该虚拟机必须是双节点部署的
- 例2:
(虚拟机ID,目的服务器ID,目的服务器节点)
- (4, 1, A)表示将 ID 为 4 的虚拟机从当前所在服务器迁移至 ID 为 1 的服务器的 A 节点,该虚拟机必须是单节点部署的。
- 接下来,只需要更具输入来合理的配置该虚拟机所在的服务器即可
- 格式为(服务器ID)或者(服务器ID,部署节点)
- 如果是双节点,则直接输出服务器id
- 如果是单节点,则需要说出节点(A或B)
- 第一个数:
三、易错点提前解析
1.Q的值
在每天的购买服务器之后,我们就需要输出当天购买服务器的类型和每台服务器的台数,因此每天输出的第一行就是(purchase, Q)
,而这个Q就是我们当天购买服务器的总类型数量。
2. 服务器id问题
在每一天,你需要提前说明需要购买的服务器种类,然后在购买完以后再(purchase, Q)
下一次输出每种服务器的个数。也就在这时,在后台和你自己这里就需要知道,从第一个服务器输出开始,就表示它的服务器id了,
比如说:你在第一天。第一次购买了A服务器(该服务器的id为0),然后又购买了新的B服务器(该服务器的id为1),但是还是不够,你就又重新购买了新的服务器A。但是这个时候A的id应该是啥?是继续B后面的id还是A后面的id。按照大赛的规则:对于第i天,如果已经购买了服务器A和B,但再次基础上又想添加一台A,那么在这台A服务器的id必须紧跟在之前购买A的服务器id后。也就是说,如果以前的A为0,B为1。如果又重新买了A服务器,则这台服务器的id必须为1,以前买的服务器id就得变成2。
那如何解决这个问题呢?下面是我的解决思路:
- 先获取这一天每天的增加服务器命令
- 然后依次执行该命令。如果当前服务器够使用,那就直接减去即可。但如果当前服务器不够,那就得重新购买新的服务器。
- 对于服务器id,我们就给与新的服务器id一个伪id(大赛规定佛购买服务器的总量不能超过10w,那我们的服务器id就从10w开始,给每个服务器一段5000的间隔(这个意思是假设这一天购买的服务器规定最多5000台,当然可以增加也可以减少))。我们将虚拟机的配置信息和伪id与挂载节点一起存起来。
- 当一天结束以后,你就可以获得当天的总购买服务器id表,这里存放着当天所购买的所有服务器总类和个数。之后我们更具存放的顺序,依次用新的服务器id替换伪id。
3. 迁移问题
我想大家在写题目的时候,遇到最多的问题就是服务器资源超出限制。但是你明明已经在迁移的时候设置好了各种限定条件,但是为什么还会出现迁移资源超出限制呢?
这是对于判题器来说,他是先进行购买服务器然后再进行迁移最后执行添加和删除操作的。而我自己(可能大多数人和我一样)是先进行执行添加操作(如果服务器不够则购买),然后进行删除最后迁移。但是这样就出现了一个问题,对于你提前删除虚拟机的服务器,在你的后台资源是增加了,但是对于判题器来说它并没有任何服务器的资源增加。这也就导致了你自己的输出是没有任何问题的,但一提交就会出现资源超出的问题。
四、思路分析
下图是最开始我们的解题思路
但是,由于迁移问题,我们需要先执行迁移,然后再进行虚拟机的添加和删除操作。也就是下面的流程
当我们的思路整理完成以后,那就可以来预定义一下我们所需要的一些资源状态。
五、参数声明
这里面,我们需要一个变量看来存储所有的服务器,同时也需要一个变量来存储所有的虚拟机
- 所有的服务器类型:
hostMapAll = {hostType: [hostCpuNum, hostMemSize, hostHardWareCost, hostDayCost]}
- 所有的虚拟机种类:
vmMapAll = {vmType: [vmCpuNum, vmMemSize, vmDoubleOrNot]}
当这些有了以后,我们需要知道已经购买的服务器id和它的资源剩余量
- 已存在的服务器列表:模板:id:{},
alExHostId = {homeId: [hostType, leaveACpu, leaveAMem, leaveBCpu, leaveBMen]}
。
同时,虚拟机的配置信息也是必须存储的一个点。
- 已存在的虚拟机列表:模板:id:{},
alExVm = {vmId: [VmType, hostId, node]}
有这些还远远不够,我们还需要一个变量来存储今天所购买的所有服务器总类与购买的数量
- 当天购买的服务器类型与数量:模板:dayByNewHost = {0: []};说明:这里0用来存储的是添加的服务器顺序,因为字典是非顺序存储的,因此很容易为我们后期修改id造成困扰,直接使用0这个量来存储当天购买服务器的种类,而后面用服务器类型来存储该服务器类型所购买的数量即可
当我们有了这些之后,当天服务器的迁移数量与每天添加服务器的配置信息也得进行存储,我们就可以使用日志来进行存储
- 存放每天的日志:
dayLevel = [[],[]]
剩余的这写都是一些比较零散的,所以就一起出来了,下面有相应的解释
hostLeaveAndVmMap = {hostId:[]} # 用来记录服务器id与虚拟机的对应情况
CostHost = [hostType, cost] # 按照服务器的性能进行升序排列的服务器性能表
hostnewID = 0 # 用来记录当前服务器的id
vmMapHost = "" # 用来记录每天的伪服务器id与虚拟机id的对应情况
resHostEx = [] # (hostid, hostLeave), up,按照服务器剩余资源进行升序排列(用于迁移时使用)
protectHostId = set() # 对于迁移时,如果出现该服务器已经没有任何虚拟机了,我们就将其加入当天迁移的保护服务器当中,也就不会在被任何操作
上面就是本次所需要使用的全部参数,下面就可以开始具体的代码编写了
六、代码实现
1. 服务器性价比代码
因为服务器有许多种,我们就需要提前对服务器进行一个了解,我使用的方法如下:
服务器性价比值=服务器CPU核数+服务器内存总量服务器总价格+服务器总价格服务器每日消耗服务器性价比值=\frac{服务器CPU核数+服务器内存总量}{服务器总价格} + \frac{服务器总价格}{服务器每日消耗} 服务器性价比值=服务器总价格服务器CPU核数+服务器内存总量+服务器每日消耗服务器总价格
使用这个公式的意思在于,在服务器总价格固定的情况下,我们希望cpu与内存多的与每日消耗少的被我们使用。那下面就是python实现的代码
def checkVal():def takeSecond(elem):return elem[1]for hostType in hostMapAll:host = hostMapAll[hostType]code = (host[0] + host[1]) / host[2] + host[2] / host[3]CostHost.append((hostType, code))CostHost.sort(key=takeSecond, reverse=True) # 更具所求出值进行升序排列
2. 最佳服务器的获取
在购买服务器时,我们就需要知道那个服务器是适合我们的。因为知道一个服务器的资源需要进行负载均衡,因此我们的所需服务器的资源至少得是虚拟机的2倍,但是有些服务器过小,可能导致服务器买的过小而不符合我们的需求,因此可以提前加一个判断,如果资源小于100,那就使用4倍于自己的,否则就是自身2倍。
def checkBestHost(vmNeedCpu, vmNeedMem):# 经过比对,获取最优的服务器for host_type, _ in CostHost: # 依次从最有的host中查看是否符合规格:if vmNeedCpu < 100 and vmNeedMem < 200:if hostMapAll[host_type][0] >= vmNeedCpu * 4 and hostMapAll[host_type][1] >= vmNeedMem * 4:BestHostType = host_typebreakelse:if hostMapAll[host_type][0] >= vmNeedCpu * 2 and hostMapAll[host_type][1] >= vmNeedMem * 2:BestHostType = host_typebreakreturn BestHostType
上面这参数在适用了好几种方案后最终确定下来,我们试过对小于10的资源进行10被甚至40被否无法更好的达到预期目标。也就这是目前最优的。
3. 删除虚拟机
对于删除虚拟机,我们只需要知道他的虚拟机id即可,然后根据id去查找它的挂在服务器与节点,增加相应的容量即可
def delVm(vmId):vmHostID = alExVm[vmId][1] # 获取该虚拟机挂在的的服务器iddelVmType = alExVm[vmId][0] # 获取该虚拟机的类型node = alExVm[vmId][2] # 获取该虚拟机挂在的节点vmCpu = vmMapAll[delVmType][0] # 获取该虚拟机所消耗的cpuvmMem = vmMapAll[delVmType][1] # 获取该虚拟机所消耗的内存# 删除操作:存在的vm进行删除,然后对应的存在host添加对应的cpu和内存del alExVm[vmId]if node == "A": # 如果节点在A,则添加A上面的消耗alExHost[vmHostID][1] += vmCpualExHost[vmHostID][2] += vmMemelif node == "B": # 如果节点在B,则添加B上面的消耗alExHost[vmHostID][3] += vmCpualExHost[vmHostID][4] += vmMemelse: # 如果节点是双节点,则经过负载均衡,A和B都加上vmCpu /= 2vmMem /= 2alExHost[vmHostID][1] += vmCpualExHost[vmHostID][2] += vmMemalExHost[vmHostID][3] += vmCpualExHost[vmHostID][4] += vmMem
4. 执行添加虚拟机算法
对于该算法,我们需要在已存在的服务器里面进行操作,如果资源充足则进行挂载。如果资源不足,就需要购买新的服务器,然后对该服务器进行相应的资源删除与记录。
def checkAndBuyHost(vmType):global vmMapHost# 1. 循环判断当前的已存在服务器,是否有剩余空间满足该虚拟机vmNeedCpu = vmMapAll[vmType][0] # vm需要的cpu量vmNeedMem = vmMapAll[vmType][1] # vm需要的内存量if vmMapAll[vmType][2] == 0: # 如果是单节点for exHostID in alExHost: # 循环查看已存在的服务器if vmNeedCpu <= alExHost[exHostID][1] and vmNeedMem <= alExHost[exHostID][2]:alExHost[exHostID][1] -= vmNeedCpu # 在已存在的服务器列表里面,减去相应的内存和cpualExHost[exHostID][2] -= vmNeedMem# 往映射表中添加三个信息vmMapHost += "{},{},{}|".format(vmType, exHostID, "A")breakif vmNeedCpu <= alExHost[exHostID][3] and vmNeedMem <= alExHost[exHostID][4]:alExHost[exHostID][3] -= vmNeedCpu # 在已存在的服务器列表里面,减去相应的内存和cpualExHost[exHostID][4] -= vmNeedMem# 往映射表中添加三个信息vmMapHost += "{},{},{}|".format(vmType, exHostID, "B")breakelse:# 当前的内存不够,需要重新购买服务器# 获取最适合的服务器类型bestHostType = checkBestHost(vmNeedCpu, vmNeedMem)hostId = buyHosy(bestHostType)HostCpu = hostMapAll[bestHostType][0] / 2HostMem = hostMapAll[bestHostType][1] / 2alExHost[hostId] = [bestHostType, HostCpu - vmNeedCpu, HostMem - vmNeedMem, HostCpu, HostMem]vmMapHost += "{},{},{}|".format(vmType, hostId, "A")else: # 双节点vmNeedCpu /= 2vmNeedMem /= 2for exHostID in alExHost:if vmNeedCpu <= min(alExHost[exHostID][1], alExHost[exHostID][3]) and vmNeedMem <= min(alExHost[exHostID][2], alExHost[exHostID][4]):alExHost[exHostID][1] -= vmNeedCpualExHost[exHostID][2] -= vmNeedMemalExHost[exHostID][3] -= vmNeedCpualExHost[exHostID][4] -= vmNeedMemvmMapHost += "{},{},{}|".format(vmType, exHostID, 0)breakelse:bestHostType = checkBestHost(vmNeedCpu, vmNeedMem)hostId = buyHosy(bestHostType)HostCpu = (hostMapAll[bestHostType][0] / 2) - vmNeedCpuHostMem = (hostMapAll[bestHostType][1] / 2) - vmNeedMemalExHost[hostId] = [bestHostType, HostCpu, HostMem, HostCpu, HostMem]vmMapHost += "{},{},{}|".format(vmType, hostId, 0)
5. 整合服务器算法
因为我们购买完服务器之后,给的是一个伪的虚拟机id,因此我们需要对其重新整合
def intergrationNewAndOldHost():global hostnewID, vmMapHost# 该函数用来整合当天需要购买的服务器与之前已存在的服务器# 应为当天购买的服务器id是从500000开始的,我们需要将其变成上一天的服务器id接下去# 这里来记录当天的购买服务器情况,放入daylevelfor i in range(len(dayByNewHost[0])):for hostId in range(dayByNewHost[dayByNewHost[0][i]]):hostId += 500000 + 5000 * ivmMapHost = vmMapHost.replace("{}".format(hostId), "{}".format(hostnewID))alExHost[hostnewID] = alExHost[hostId]hostLeaveAndVmMap[hostnewID] = []del alExHost[hostId]hostnewID += 1vmMapHost = vmMapHost.split("|")vmMapHost.reverse()
6. 设置虚拟机
def setVm(vm_id):global vmMapHosttip = vmMapHost.pop()tip = tip.split(",") # vmType, hostid, nodealExVm[vm_id] = [tip[0], int(tip[1]), tip[2]] # 添加已经存在虚拟机hostLeaveAndVmMap[int(tip[1])].append(vm_id)if tip[2] == "0":dayLevel[1].append("({})".format(tip[1])) # 添加日志else:dayLevel[1].append("({},{})".format(tip[1], tip[2]))
7. 迁移算法
def checkMigrationHost():def takeSecond(elem):return elem[1]for hostId in alExHost:resHostEx.append((hostId, sum(alExHost[hostId][1:])))resHostEx.sort(key=takeSecond)def checkMigrationVm(VmList):def takeSecond(elem):return elem[1]newList = []for vmId in VmList:newList.append((vmId, sum(vmMapAll[alExVm[vmId][0]][:2])))newList.sort(key=takeSecond, reverse=True) # 降序排列for i in range(len(newList)):newList[i] = newList[i][0]return newListdef migrationVm():hostIdTip = 24 * len(resHostEx) // 25 # 初始迁移的服务器,默认24/25台开始canMigrationHostIdTip = hostIdTip - 1 # 可以迁移的服务器从当前迁移的服务器前一台开始# 判断条件,只要迁移服务器小于总服务器的长度while len(resHostEx) > hostIdTip:hostId = resHostEx[hostIdTip][0] # 获取需要迁移服务器的idcanMigrationHostId = resHostEx[canMigrationHostIdTip][0] # 获取可以被迁移服务器的idcanMigrationHostLeave = resHostEx[canMigrationHostIdTip][1] # 获取可以被迁移服务器的剩余资源# 如果该服务器是保护服务器if canMigrationHostId in protectHostId:canMigrationHostIdTip -= 1continue# 如果当前服务器到达第一个或者当前服务器的资源已经为0if canMigrationHostIdTip < 0 or canMigrationHostLeave == 0:hostIdTip += 1canMigrationHostIdTip = hostIdTip - 1continue# 如果需要迁移服务器的内部已经没有虚拟机,则认为这个服务器今天不能被放入新的虚拟机,则需要将其保护起来if len(hostLeaveAndVmMap[hostId]) == 0:protectHostId.add(hostId)hostIdTip += 1canMigrationHostIdTip = hostIdTip - 1continue# 开始对服务器内的虚拟机进行迁移newList = checkMigrationVm(hostLeaveAndVmMap[hostId]) # 按照服务器所需资源的大小进行升序排列for vmId in newList:vm = vmMapAll[alExVm[vmId][0]] # 通过id获取该虚拟机vmCpu = vm[0]vmMem = vm[1]vmNode = vm[2]# 如果当前剩余服务器的资源小于该虚拟机所需内存,则直接跳出循环(当前需要迁移的服务器已经很不可能进行迁移了,所以直接退出,换下一个服务器)if canMigrationHostLeave < vmCpu + vmNode:hostIdTip += 1canMigrationHostIdTip = hostIdTip - 1breakif vmNode == 0:exNode = alExVm[vmId][2] # 原先该虚拟机存放的节点# 单节点if vmCpu <= alExHost[canMigrationHostId][1] and vmMem <= alExHost[canMigrationHostId][2]:# 说明canMigrationHostId的A节点可以存放该虚拟机# 对旧的服务器提升内存空间if exNode == "A":alExHost[hostId][1] += vmCpualExHost[hostId][2] += vmMemif exNode == "B":alExHost[hostId][3] += vmCpualExHost[hostId][4] += vmMem# 对新的服务器减少空间alExHost[canMigrationHostId][1] -= vmCpualExHost[canMigrationHostId][2] -= vmMem# 对虚拟机的映射节点进行更换alExVm[vmId][1] = canMigrationHostIdalExVm[vmId][2] = "A"hostLeaveAndVmMap[canMigrationHostId].append(vmId)hostLeaveAndVmMap[hostId].remove(vmId)# 添加日志dayLevel[0].append("({},{},{})".format(vmId, canMigrationHostId, 'A'))return 1elif vmCpu <= alExHost[canMigrationHostId][3] and vmMem <= alExHost[canMigrationHostId][4]:# 对旧的服务器提升内存空间if exNode == "A":alExHost[hostId][1] += vmCpualExHost[hostId][2] += vmMemif exNode == "B":alExHost[hostId][3] += vmCpualExHost[hostId][4] += vmMem# 对新的服务器减少空间alExHost[canMigrationHostId][3] -= vmCpualExHost[canMigrationHostId][4] -= vmMem# 对虚拟机的映射节点进行更换alExVm[vmId][1] = canMigrationHostIdalExVm[vmId][2] = "B"hostLeaveAndVmMap[canMigrationHostId].append(vmId)hostLeaveAndVmMap[hostId].remove(vmId)dayLevel[0].append("({},{},{})".format(vmId, canMigrationHostId, 'B'))return 1else:# 双节点vmCpu /= 2vmMem /= 2if vmCpu <= alExHost[canMigrationHostId][1] and vmMem <= alExHost[canMigrationHostId][2] and vmCpu <= alExHost[canMigrationHostId][3] and vmMem <= alExHost[canMigrationHostId][4]:# 对旧的服务器提升内存空间alExHost[hostId][1] += vmCpualExHost[hostId][2] += vmMemalExHost[hostId][3] += vmCpualExHost[hostId][4] += vmMem# 对新的服务器减少空间alExHost[canMigrationHostId][1] -= vmCpualExHost[canMigrationHostId][2] -= vmMemalExHost[canMigrationHostId][3] -= vmCpualExHost[canMigrationHostId][4] -= vmMem# 对虚拟机的映射节点进行更换alExVm[vmId][1] = canMigrationHostIdhostLeaveAndVmMap[canMigrationHostId].append(vmId)hostLeaveAndVmMap[hostId].remove(vmId)newList.remove(vmId)dayLevel[0].append("({},{})".format(vmId, canMigrationHostId))return 1else:hostIdTip += 1canMigrationHostIdTip = hostIdTip - 1return 0 # 表示现在的服务器已经是最优,并没有任何的空余空间可以转移了
8. 获取服务器与虚拟机算法
N = int(input())
for _ in range(N):host = input().replace(" ", "").replace("\n", "").replace("(", "").replace(")", "").split(",") # (host78K52, 788, 838, 251170, 314)hostMapAll[host[0]] = [int(host[1]), int(host[2]), int(host[3]), int(host[4])]checkVal() # 查看所有服务器的性价比# 获取所有的虚拟机
M = int(input())
for _ in range(M):vm = input().replace(" ", "").replace("\n", "").replace("(", "").replace(")", "").split(",")vmMapAll[vm[0]] = [int(vm[1]), int(vm[2]), int(vm[3])]days = int(input()) # 总共的天数
七、总代码
上面讲完每一步的算法之后,下面就是这次的总代码了
"""hostMapAll = {hostType: [hostCpuNum, hostMemSize, hostHardWareCost, hostDayCost]} # 存放所有的服务器vmMapAll = {vmType: [vmCpuNum, vmMemSize, vmDoubleOrNot]} # 存放所有的虚拟机alExHost = {hostId: [hostType, leaveACpu, leaveAMem, leaveBCpu, leaveBMen]} # 存放已存在的服务器alExVm = {vmId: [VmType, hostId, node]} # 存放已存在的虚拟机hostLeaveAndVmMap = {hostId:[]} # 用来记录服务器id与虚拟机的对应情况CostHost = [hostType, cost] # 按照服务器的性能进行升序排列的服务器性能表dayLevel = [[],[]] # 存放每天的日志(迁移量和所给的虚拟机配置量)
"""hostMapAll = {}
vmMapAll = {}
alExHost = {}
alExVm = {}
dayLevel = [[], []]
hostLeaveAndVmMap = {}
hostnewID = 0
CostHost = []
vmMapHost = ""
dayByNewHost = {0: []}
resHostEx = [] # (hostid, hostLeave), up
protectHostId = set()# host1WJCI [334, 334, 96465, 120]
def checkVal(): # host0XKXWdef takeSecond(elem):return elem[1]for hostType in hostMapAll:host = hostMapAll[hostType]code = (host[0] + host[1]) / host[2] + host[2] / host[3]CostHost.append((hostType, code))CostHost.sort(key=takeSecond, reverse=True) # 更具所求出值进行升序排列def checkBestHost(vmNeedCpu, vmNeedMem):# 经过比对,获取最优的服务器for host_type, _ in CostHost: # 依次从最有的host中查看是否符合规格:# if vmNeedCpu < 100 and vmNeedMem < 100:# if hostMapAll[host_type][0] >= vmNeedCpu * 5 and hostMapAll[host_type][1] >= vmNeedMem * 4:# BestHostType = host_type# break# else:if hostMapAll[host_type][0] >= vmNeedCpu * 2 and hostMapAll[host_type][1] >= vmNeedMem * 2:BestHostType = host_typebreakreturn BestHostTypedef setVm(vm_id):global vmMapHosttip = vmMapHost.pop()tip = tip.split(",") # vmType, hostid, nodealExVm[vm_id] = [tip[0], int(tip[1]), tip[2]] # 添加已经存在虚拟机hostLeaveAndVmMap[int(tip[1])].append(vm_id)if tip[2] == "0":dayLevel[1].append("({})".format(tip[1])) # 添加日志else:dayLevel[1].append("({},{})".format(tip[1], tip[2]))def delVm(vmId):"""vmMapAll = {vmType: [vmCpuNum, vmMemSize, vmDoubleOrNot]}alExVm = {vmId: [vmType, hostId, node]}alExHost = {homeId: [hostType, leaveACpu, leaveAMem, leaveBCpu, leaveBMen]}:return:"""vmHostID = alExVm[vmId][1] # 获取该虚拟机挂在的的服务器iddelVmType = alExVm[vmId][0] # 获取该虚拟机的类型node = alExVm[vmId][2] # 获取该虚拟机挂在的节点vmCpu = vmMapAll[delVmType][0] # 获取该虚拟机所消耗的cpuvmMem = vmMapAll[delVmType][1] # 获取该虚拟机所消耗的内存hostLeaveAndVmMap[vmHostID].remove(vmId)# 删除操作:存在的vm进行删除,然后对应的存在host添加对应的cpu和内存del alExVm[vmId]if node == "A": # 如果节点在A,则添加A上面的消耗alExHost[vmHostID][1] += vmCpualExHost[vmHostID][2] += vmMemelif node == "B": # 如果节点在B,则添加B上面的消耗alExHost[vmHostID][3] += vmCpualExHost[vmHostID][4] += vmMemelse: # 如果节点是双节点,则经过负载均衡,A和B都加上vmCpu /= 2vmMem /= 2alExHost[vmHostID][1] += vmCpualExHost[vmHostID][2] += vmMemalExHost[vmHostID][3] += vmCpualExHost[vmHostID][4] += vmMemdef checkMigrationHost():def takeSecond(elem):return elem[1]for hostId in alExHost:resHostEx.append((hostId, sum(alExHost[hostId][1:])))resHostEx.sort(key=takeSecond)def checkMigrationVm(VmList):def takeSecond(elem):return elem[1]newList = []for vmId in VmList:newList.append((vmId, sum(vmMapAll[alExVm[vmId][0]][:2])))newList.sort(key=takeSecond, reverse=True) # 降序排列for i in range(len(newList)):newList[i] = newList[i][0]return newListdef migrationVm():hostIdTip = 24 * len(resHostEx) // 25 # 初始迁移的服务器,默认24/25台开始canMigrationHostIdTip = hostIdTip - 1 # 可以迁移的服务器从当前迁移的服务器前一台开始# 判断条件,只要迁移服务器小于总服务器的长度while len(resHostEx) > hostIdTip:hostId = resHostEx[hostIdTip][0] # 获取需要迁移服务器的idcanMigrationHostId = resHostEx[canMigrationHostIdTip][0] # 获取可以被迁移服务器的idcanMigrationHostLeave = resHostEx[canMigrationHostIdTip][1] # 获取可以被迁移服务器的剩余资源# 如果该服务器是保护服务器if canMigrationHostId in protectHostId:canMigrationHostIdTip -= 1continue# 如果当前服务器到达第一个或者当前服务器的资源已经为0if canMigrationHostIdTip < 0 or canMigrationHostLeave == 0:hostIdTip += 1canMigrationHostIdTip = hostIdTip - 1continue# 如果需要迁移服务器的内部已经没有虚拟机,则认为这个服务器今天不能被放入新的虚拟机,则需要将其保护起来if len(hostLeaveAndVmMap[hostId]) == 0:protectHostId.add(hostId)hostIdTip += 1canMigrationHostIdTip = hostIdTip - 1continue# 开始对服务器内的虚拟机进行迁移newList = checkMigrationVm(hostLeaveAndVmMap[hostId]) # 按照服务器所需资源的大小进行升序排列for vmId in newList:vm = vmMapAll[alExVm[vmId][0]] # 通过id获取该虚拟机vmCpu = vm[0]vmMem = vm[1]vmNode = vm[2]# 如果当前剩余服务器的资源小于该虚拟机所需内存,则直接跳出循环(当前需要迁移的服务器已经很不可能进行迁移了,所以直接退出,换下一个服务器)if canMigrationHostLeave < vmCpu + vmNode:hostIdTip += 1canMigrationHostIdTip = hostIdTip - 1breakif vmNode == 0:exNode = alExVm[vmId][2] # 原先该虚拟机存放的节点# 单节点if vmCpu <= alExHost[canMigrationHostId][1] and vmMem <= alExHost[canMigrationHostId][2]:# 说明canMigrationHostId的A节点可以存放该虚拟机# 对旧的服务器提升内存空间if exNode == "A":alExHost[hostId][1] += vmCpualExHost[hostId][2] += vmMemif exNode == "B":alExHost[hostId][3] += vmCpualExHost[hostId][4] += vmMem# 对新的服务器减少空间alExHost[canMigrationHostId][1] -= vmCpualExHost[canMigrationHostId][2] -= vmMem# 对虚拟机的映射节点进行更换alExVm[vmId][1] = canMigrationHostIdalExVm[vmId][2] = "A"hostLeaveAndVmMap[canMigrationHostId].append(vmId)hostLeaveAndVmMap[hostId].remove(vmId)# 添加日志dayLevel[0].append("({},{},{})".format(vmId, canMigrationHostId, 'A'))return 1elif vmCpu <= alExHost[canMigrationHostId][3] and vmMem <= alExHost[canMigrationHostId][4]:# 对旧的服务器提升内存空间if exNode == "A":alExHost[hostId][1] += vmCpualExHost[hostId][2] += vmMemif exNode == "B":alExHost[hostId][3] += vmCpualExHost[hostId][4] += vmMem# 对新的服务器减少空间alExHost[canMigrationHostId][3] -= vmCpualExHost[canMigrationHostId][4] -= vmMem# 对虚拟机的映射节点进行更换alExVm[vmId][1] = canMigrationHostIdalExVm[vmId][2] = "B"hostLeaveAndVmMap[canMigrationHostId].append(vmId)hostLeaveAndVmMap[hostId].remove(vmId)dayLevel[0].append("({},{},{})".format(vmId, canMigrationHostId, 'B'))return 1else:# 双节点vmCpu /= 2vmMem /= 2if vmCpu <= alExHost[canMigrationHostId][1] and vmMem <= alExHost[canMigrationHostId][2] and vmCpu <= alExHost[canMigrationHostId][3] and vmMem <= alExHost[canMigrationHostId][4]:# 对旧的服务器提升内存空间alExHost[hostId][1] += vmCpualExHost[hostId][2] += vmMemalExHost[hostId][3] += vmCpualExHost[hostId][4] += vmMem# 对新的服务器减少空间alExHost[canMigrationHostId][1] -= vmCpualExHost[canMigrationHostId][2] -= vmMemalExHost[canMigrationHostId][3] -= vmCpualExHost[canMigrationHostId][4] -= vmMem# 对虚拟机的映射节点进行更换alExVm[vmId][1] = canMigrationHostIdhostLeaveAndVmMap[canMigrationHostId].append(vmId)hostLeaveAndVmMap[hostId].remove(vmId)newList.remove(vmId)dayLevel[0].append("({},{})".format(vmId, canMigrationHostId))return 1else:hostIdTip += 1canMigrationHostIdTip = hostIdTip - 1return 0 # 表示现在的服务器已经是最优,并没有任何的空余空间可以转移了def buyHosy(hostType):# 给与当前购买的服务器虚拟idn = dayByNewHost.get(hostType, 0)if n == 0: # 说明,当前的服务器类型并不存在hostId = 500000 + len(dayByNewHost[0]) * 5000dayByNewHost[0].append(hostType)else: # 说明已经存在# 利用公式算出当前服务器的值hostId = 500000for hostT in dayByNewHost[0]:if hostType == hostT:hostId += nbreakelse:hostId += 5000dayByNewHost[hostType] = n + 1return hostIddef checkAndBuyHost(vmType):global vmMapHost# 1. 循环判断当前的已存在服务器,是否有剩余空间满足该虚拟机vmNeedCpu = vmMapAll[vmType][0] # vm需要的cpu量vmNeedMem = vmMapAll[vmType][1] # vm需要的内存量if vmMapAll[vmType][2] == 0: # 如果是单节点for exHostID in alExHost: # 循环查看已存在的服务器if vmNeedCpu <= alExHost[exHostID][1] and vmNeedMem <= alExHost[exHostID][2]:alExHost[exHostID][1] -= vmNeedCpu # 在已存在的服务器列表里面,减去相应的内存和cpualExHost[exHostID][2] -= vmNeedMem# 往映射表中添加三个信息vmMapHost += "{},{},{}|".format(vmType, exHostID, "A")breakif vmNeedCpu <= alExHost[exHostID][3] and vmNeedMem <= alExHost[exHostID][4]:alExHost[exHostID][3] -= vmNeedCpu # 在已存在的服务器列表里面,减去相应的内存和cpualExHost[exHostID][4] -= vmNeedMem# 往映射表中添加三个信息vmMapHost += "{},{},{}|".format(vmType, exHostID, "B")breakelse:# 当前的内存不够,需要重新购买服务器# 获取最适合的服务器类型bestHostType = checkBestHost(vmNeedCpu, vmNeedMem)hostId = buyHosy(bestHostType)HostCpu = hostMapAll[bestHostType][0] / 2HostMem = hostMapAll[bestHostType][1] / 2alExHost[hostId] = [bestHostType, HostCpu - vmNeedCpu, HostMem - vmNeedMem, HostCpu, HostMem]vmMapHost += "{},{},{}|".format(vmType, hostId, "A")else: # 双节点vmNeedCpu /= 2vmNeedMem /= 2for exHostID in alExHost:if vmNeedCpu <= min(alExHost[exHostID][1], alExHost[exHostID][3]) and vmNeedMem <= min(alExHost[exHostID][2], alExHost[exHostID][4]):alExHost[exHostID][1] -= vmNeedCpualExHost[exHostID][2] -= vmNeedMemalExHost[exHostID][3] -= vmNeedCpualExHost[exHostID][4] -= vmNeedMemvmMapHost += "{},{},{}|".format(vmType, exHostID, 0)breakelse:bestHostType = checkBestHost(vmNeedCpu, vmNeedMem)hostId = buyHosy(bestHostType)HostCpu = (hostMapAll[bestHostType][0] / 2) - vmNeedCpuHostMem = (hostMapAll[bestHostType][1] / 2) - vmNeedMemalExHost[hostId] = [bestHostType, HostCpu, HostMem, HostCpu, HostMem]vmMapHost += "{},{},{}|".format(vmType, hostId, 0)def intergrationNewAndOldHost():global hostnewID, vmMapHost# 该函数用来整合当天需要购买的服务器与之前已存在的服务器# 应为当天购买的服务器id是从500000开始的,我们需要将其变成上一天的服务器id接下去# 这里来记录当天的购买服务器情况,放入daylevelfor i in range(len(dayByNewHost[0])):for hostId in range(dayByNewHost[dayByNewHost[0][i]]):hostId += 500000 + 5000 * ivmMapHost = vmMapHost.replace("{}".format(hostId), "{}".format(hostnewID))alExHost[hostnewID] = alExHost[hostId]hostLeaveAndVmMap[hostnewID] = []del alExHost[hostId]hostnewID += 1vmMapHost = vmMapHost.split("|")vmMapHost.reverse()def main():N = int(input())for _ in range(N):host = input().replace(" ", "").replace("\n", "").replace("(", "").replace(")", "").split(",") # (host78K52, 788, 838, 251170, 314)hostMapAll[host[0]] = [int(host[1]), int(host[2]), int(host[3]), int(host[4])]checkVal() # 查看所有服务器的性价比# 获取所有的虚拟机M = int(input())for _ in range(M):vm = input().replace(" ", "").replace("\n", "").replace("(", "").replace(")", "").split(",")vmMapAll[vm[0]] = [int(vm[1]), int(vm[2]), int(vm[3])]days = int(input()) # 总共的天数# 更具总共的天数,来进行每天的操作for day in range(1, days + 1):global vmMapHostallDo = int(input())addDo = [] # [vmId]delDo = [] # [vmId]checkMigrationHost()total = 1while total + 1 < len(alExVm) // 200:num = migrationVm()if num == 1:total += numelse:breakresHostEx.clear()protectHostId.clear()# 进行每天的操作for _ in range(allDo):tip = input().replace(" ", "").replace("\n", "").replace("(", "").replace(")", "").split(",")if tip[0] == "add": # 将添加操作放入列表,然后进行当前服务器内存的检查checkAndBuyHost(tip[1])addDo.append(int(tip[2]))else: # 把删除操作的放入列表delDo.append(tip[1:][0])intergrationNewAndOldHost() # 进行服务器的整合# 执行添加操作for vmId in addDo:setVm(vmId)# 执行删除操作for vmId in delDo:delVm(int(vmId))# 打印当天的处理结果print("(purchase, {})".format(len(dayByNewHost) - 1))for i in dayByNewHost:if i == 0:continueprint("({},{})".format(i, dayByNewHost[i]))print("(migration, {})".format(len(dayLevel[0])))for i in dayLevel[0]:print(i)for i in dayLevel[1]:print(i)dayLevel[0].clear()dayLevel[1].clear()dayByNewHost.clear()dayByNewHost[0] = []vmMapHost = ""if __name__ == '__main__':main()
可调的参数:
- 迁移的次数
- 迁移时的数据起点
- 服务器性价比配置算法
- 最优服务器的配置查找算法
由于本次比赛时间有限,外加自己实力不足,未能挺进32强,但是很感谢这次机会,让自己有所成长。
2021华为软挑(区域初赛)总结相关推荐
- 2021华为软挑部分答疑——哪些你有错却总是找不到的地方,我来带你找啦(含标准输入代码)
前期工作: 2021华为软挑初探--代码实现 2021华为软挑再探--代码实现 1 关于打包 在windows系统下,先把你写的程序写在src里面的CodeCraft-2021里面 然后在这个页面,将 ...
- 2022华为软挑比赛(初赛笔记)
文章目录 2022华为软挑(初赛笔记) 1. 赛题要求 2. 解决方案 2.1 挑选适合的边缘节点 2.2 第一轮:最大分配 2.3 第二轮:均值分配 总结 本文仓库地址: Github-CodeCr ...
- O准备如何苟进复赛圈?华为软挑开挂指南(附赛题预测)
这篇文章纯粹是心血来潮,原因是去年上传到github的参赛代码,前几天又有两个人star和fork了. 记得star热潮还是去年4月复赛刚结束的那几天,厚着脸皮在八大赛区的群里发消息求关注,之后就陷入 ...
- 华为软挑赛2023-复赛笔记
前言 比赛介绍 参考初赛博客: 华为软挑赛2023-初赛笔记_没有梦想的大白兔的博客-CSDN博客 赛题变动 官网赛题: 华为云论坛_云计算论坛_开发者论坛_技术论坛-华为云 (huaweicloud ...
- 2020华为软挑热身赛代码开源-思路大起底(华为软件精英挑战赛编程闯关)
本文首发于个人公众号[两猿社],后台回复[华为],获取完整开源代码链接. 昵称:lou_shang_shi_bian_tai 成绩:0.032 社长没有针对硬件做任何优化,热身赛成绩也一般.但有些比赛 ...
- 华为软挑赛2023-初赛笔记
前言 比赛介绍 官方链接: 2023华为软件精英挑战赛--普朗克计划 (huaweicloud.com) 赛题介绍 场景介绍 官方赛题介绍: 2023华为软件精英挑战赛初赛赛题及相关材料发布_2023 ...
- 2021华为软件精英挑战赛初赛baseline
2021华为软件精英挑战赛初赛baseline,由ddmm大佬提供的单文件baseline按照工程开发格式改写,改为以类为单位,多文件的格式.同样没有在里面添加任何算法,相当于一个脚手架,帮助大家更快 ...
- 2020华为软挑热身赛-这些坑我帮你踩过了(华为软件精英挑战赛编程闯关)
本文始发于个人公众号[两猿社]. 声明,为保证比赛公平,本文不会提供比赛源码,仅提供思路与踩坑经验. 他来了,他来了,他带着面试绿卡走来了. 他来了,他来了,他带着20w大奖走来了. 一年一度的华为软 ...
- 2019华为软挑代码总结-交通规则模拟
文章目录 前言 三.数据读入后怎么跑呢? 总结 四.怎么看效果? 前言 以前一份代码的阅读,2019华为软挑 三.数据读入后怎么跑呢? 思路: 1.创建地图类,将地图中每个路口到其他路口最短路径保存. ...
- 2021华为软件精英挑战赛初赛代码及思路
2021华为软件精英挑战赛训练赛.正式赛思路分享 有幸再次参加了华为软件精英挑战赛(去年由于不知道数据集有坑,导致没能进入复赛,今年决定再来一次弥补去年的遗憾) 今年的赛题相比去年个人 ...
最新文章
- Python中输出字体的颜色设置
- Selenium+python --获取百度联想词
- 列举在移动Web开发中经常会设置的特殊样式!
- Android开源底部导航,一个开源JPTabBar for Android,炫酷的底部导航栏
- 如何逃离「信息茧房」?
- 棋牌类游戏算法–牌分类_快速分类–三向和双枢轴
- java 异步调用 shell_Java 实现异步调用
- 作者:许方圆,男,国网能源研究院能源决策支持技术研发中心中级工程师。...
- 30 FI配置-财务会计-外币评估-准备外币评估的自动过账
- 云主机挂载硬盘 - 开机自动挂载
- services.xml应该放在项目的哪里_新轮胎应该放在前轮还是后轮?
- Linux基金会呼吁厂商放弃微软FAT文件系统
- 高通CSRA6640单芯片DDFA放大器解决方案
- 瞬态抑制二极管有方向吗?
- Adobe 奇葩续费机制被网友狂喷:中途取消计划必须付费 50%
- 循环el-color-picker修改echarts饼图配色
- LWN:5.18合并窗口第一部分!
- 关于Spring Boot报错:LoggerFactory is not a Logback LoggerContext but Logback is on the classpath
- 《大象--Thinking in UML 第二版》已于近日在当当首发,同时邀请各位加入新浪微博[大象-thinkinginUml群]:http://q.weibo.com/1483929
- 感叹之余随手记—他山之石,可以攻玉