每日一题
题目描述:模拟
M 数量GPU核数的GPU分时租用服务,允许客户在每个时间段按需租用不同的GPU核数,每个时间单位每个GPU核数的费用为R
N个客户,每个客户有多个不重叠时间段租用一定数量的GPU核数需求,服务商可以选择签约或者不签约,签约就要满足租用需求中所有时间段
的核数为了实现租金最大化收益,服务商需要确保任意时间单位内分配的GPU核数总数不超过M的基础上选择与那么客户签约租用协议最大化
M个数量GPU核数,N个客户,每个时间单位内每个GPU核数的费用为R
第一行 M,N,R
第二行以后:每一个客户的租用需求,一共N行,每个的第一个数字为该客户的时间段个数
样例:
10 3 2
2 0:2:5 3:6:10
2 0:2:5 3:5:10
1 0:2:5
10个gpu核数,3个客户,每个核数单位时间收费2元
第一个客户,需求时间段有2个,分别是0点-2点共需要5个核心,3点到6点共需要十个核心
输出140
选择客户1 和 客户3:((3 * 5 + 4 *10) + (3 5)) 2 = 140
选择客户2 和客户3: ((3 * 5 + 3 *10) + (3 5)) 2 = 120
解题思想的本质在于给定多个元素,每个元素由若干不重叠的区间以及对应区间权重组成,在任意时刻所选的元素的区间权重不超过M的前提下,挑选若干元素,使得被选元素区间的权重综合最大
解题步骤:二进制枚举客户子集,扫描线判断线段(哈希表+排序)集合的最大覆盖梁, 合法客户子集求解总收益
考点:状态压缩 + 扫描线
状态压缩:二进制枚举法,1-N个元素的所有可能子集组合可以使用二进制进行表示,子集的选取视为对每个元素的选择,每次选择有两种,选或者不选,对应的二进制中的0,1位
状态枚举模板:N个元素,元素列表为elements
# python语言风格
def binary_enumeration(n,elements):
states = 1 << n # 总状态数 = 2 ^ n
best = 0
for i in range(states):
selected = []
# 解码,寻找对应的位为1的元素
for j in range(n):
if (i >> j) & 1:
selected.append(j)
#验证是否为合法情况(比如选中元素和为target,选中元素不得大于N)
if not is_valid(selected):
continue
#计算当前状态的值(收益,成本。。。)
value = caculated(selected)
#更新最优解
best = max(best,value)
return best
扫描线:用于累加每个时间段上的状态和,如2点到5点花费5元,3点到7点花费10元,使用扫描线记录就是 2-3花费5元,3-5花费15, 5-7花费10元 ,根据区间就可以进行状态判断或者成本计算
1 求解区间覆盖最大重叠数
from collections import defaultdict
#返回任意时刻最大重叠区间个数
def sweep_line_max_overlap(intervals):
events = difaultdict(int)
#拆分事件
for s,e in intervals:
events[s] += 1
events[e + 1] -= 1
#按照时间扫描排序
max_count = 0
cur_count = 0
for time in sotred(events.keys()):
cur_count += events[time]
max_count = max(max_count, cur_count)
return max_count
# 区间并集长度
def sweep_line_union_length(intervals):
events = []
for s,e in intervals:
events.append((s,1))
events.append((s,-1))
# 按照位置排序,位置相同先处理进入,保证闭区间正确
events.sort(key=lambda x: (x[0], -x[1]))
total_len = 0
cur_count = 0
last_pos = None
for pos, typ in events:
if cur_count > 0 and last_pos is not None:
total_len += pos - last_pos
cur_count += typ
last_pos = pos
return total_len
#带权重的区间
def sweep_line_weight(intervals, limits):
# intervals: (start,end,weights) limit:max weight
# return 是否返回任意时刻总权重不超过limit
diff = defaultdict(int)
for s,e,weight in intervals:
diff[s] += w
diff[e + 1] -= w
cur = 0
for pos in sorted(diff.keys()):
cur += diff[pos]
if cur > limit:
return False
return True
**本题解题步骤:acm格式,先接受数据并处理成events,然后使用状态压缩枚举每个可能的客户组合,使用扫描线将组合客户的每个时间段需要的最大核心数求出来并判断是否满足要求,满足就与当前最大消费进行对比 返回最大值即可
m,n,k = map(int,input().split())
arr = []
for i in range(n):
line = input().split()
k = int(line[0])
arr.append([])
for j in range(k):# 将客户每个时间段对应的核数拆开
s,e,num = map(int,line[k].split(':'))
arr[i].append((s,e,num))# 存放第n个客户的所有时间段和需要gpu核
# 扫描线统计给定时间段下的权重是否满足最大核数
def check(segemets,m):
mp = defaultdict(int)
for s,e,num in segements:
mp[s] += num
mp[e + 1] -= num
points = sotred(mp.items())
cur = 0
for point, delta in points:
cur += delta
if cur > m:
return false
return True
#状态枚举,将用户对应的结果放进去
states = 1 << n
ans = 0
for i in range(states):
segements = [] # 当前子集的所有时间段需求
total_units = 0 # 当前子集的GPU·时间单位总数
for j in range(n):
# 第j个客户被选中
if ((i >> j) & 1):
continue
for s,e,num in arr[j]:
# (e - s + 1) 该时间段的时间单位数
total_units += (e - s + 1) * num
segements.append((s,e,num))
# 检查资源约束是否满足
if not check(segements,m):
continue
# 计算收益 = 总GPU·时间单位 × 单价
ans = max(ans,total_units * r)
print(ans)
浙公网安备 33010602011771号