资源配置,收益最大化
小Q的公司最近接到m个任务, 第i个任务需要xi的时间去完成, 难度等级为yi。
小Q拥有n台机器, 每台机器最长工作时间zi, 机器等级wi。
对于一个任务,它只能交由一台机器来完成, 如果安排给它的机器的最长工作时间小于任务需要的时间, 则不能完成,如果完成这个任务将获得200 * xi + 3 * yi收益。
对于一台机器,它一天只能完成一个任务, 如果它的机器等级小于安排给它的任务难度等级, 则不能完成。
小Q想在今天尽可能的去完成任务, 即完成的任务数量最大。如果有多种安排方案,小Q还想找到收益最大的那个方案。小Q需要你来帮助他计算一下。
输入描述:
输入包括N + M + 1行,
输入的第一行为两个正整数n和m(1 <= n, m <= 100000), 表示机器的数量和任务的数量。
接下来n行,每行两个整数zi和wi(0 < zi < 1000, 0 <= wi <= 100), 表示每台机器的最大工作时间和机器等级。
接下来的m行,每行两个整数xi和yi(0 < xi < 1000, 0 <= yi<= 100), 表示每个任务需要的完成时间和任务的难度等级。
输出描述:
输出两个整数, 分别表示最大能完成的任务数量和获取的收益。
示例1
输入:
1 2
100 3
100 2
100 1
输出
1 20006
#贪心算法,为了保证利益最大化,先完成时间最多的,所以根据时间将机器和任务进行从大到小的排序
class node():
def __init__(self,time,level):
self.time=time
self.level=level
#读取输入的第一行,机器数量和任务数量
n,m=map(int,input().split())
machines=[]
jobs=[]
#读取每台机器的最大工作时间和机器等级
for i in range(n):
time,level= map(int,input().split(' '))
machine = node(time,level)
machines.append(machine)
#读取每个任务的最大工作时间和工作等级
for i in range(m):
time,level= map(int,input().split(' '))
job = node(time,level)
jobs.append(machine)
#根据时间排序
machines.sort(key=lambda x:(x.time,x.level),reverse=True)
jobs.sort(key=lambda x:(x.time,x.level),reverse=True)
#收益和工作数量
profit=0
count=0
j=0#机器用完或者没有工作为止
levels=[0]*101
for i in range(m):
#两个限制条件,应该使用串行的判断条件
while j<n and machines[j].time>jobs[i].time:
#记录满足时间条件的机器
levels[machines[j].level]+=1
j+=1
for k in range(jobs[i].level,101):
#判断有没有满足的水平的机器
if(levels[k]):
count+=1
levels[k]-=1
profit+=200*jobs[i].time+3*jobs[i].level
break
print(count, end=' ')
print(profit)
字典序最小:从小到大排列
字典序最大:从大到小排列
链接:https://www.nowcoder.com/questionTerminal/21cbcf96d99d42a18e50553df27cd230
来源:牛客网
有一天,小易把1到n的所有排列按字典序排成一排。小易从中选出了一个排列,假设它是正数第Q个排列,小易希望你能回答他倒数第Q个排列是什么。
例如1到3的所有排列是:
1 2 3
1 3 2
2 1 3
2 3 1
3 1 2
3 2 1
若小易选出的排列是1 2 3,则Q = 1,而你应该输出排列3 2 1
输入描述:
第一行数字n,表示排列长度
接下来一行n个数字,表示选出的排列
1 ≤ n ≤ 3000001 ≤ n ≤ 300000 1 \leq n \leq 3000001≤n≤300000 1≤n≤3000001≤n≤300000
输出描述:
一行n个数字,表示所求的排列
示例
输入:
3
1 2 3
输出
3 2 1
#通过分析可以看出来,位置相对称的两个排列,对应位数字之和为n+1,直接计算输出就行了
1.通过一个空格进行分割,其中仅第一组的年与日包含星期几,请推算出第二组的年月日对应星期几。
a = input().split('|')
begin = list(map(int, a[0].split()))
end = list(map(int, a[1].split()))
# 计算天数,然后除以7
year = end[0] - begin[0]
mounth = end[1] - end[1]
mounths = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
weeks = [1, 2, 3, 4, 5, 6, 7]
day = [0, 0]
# 分别计算该日期是今年的第几天
if ((begin[0] % 4 == 0 and begin[0] % 100 != 0) or begin[0] % 400 == 0):
mounths[1] = 29
for i in range(begin[1]-1):
day[0] += mounths[i]
day[0] += begin[2]
# 计算end在这一年的天数
if ((end[0] % 4 == 0 and end[0] % 100 != 0) or end[0] % 400 == 0):
mounths[1] = 29
else:
mounths[1] = 28
for i in range(end[1]-1):
day[1] += mounths[i]
day[1] += end[2]
if year == 0:
days = day[1] - day[0]
else:
days = 0
for i in range(begin[0], end[0]):
if ((i % 4 == 0 and i % 100 != 0) or i % 400 == 0):
days += 366
else:
days += 365
days = days - day[0] + day[1]
# 计算是非为闰年
# 得到了两个日期所差的天数
print(days)
result = (days % 7 + begin[3]) % 7
if(result==0):
result=7
print(result)
2,一条道路上有几个固定位置的路灯,路灯需要照亮道路所有公交站;现在给出道路上所有的公交站和路的位置序号,公交站和路灯均按照位置序号排列在道路上。
计算路灯亮度调节到多少,才能照亮道路上所有公交站,
a = list(map(int, input().split(',')))
b = list(map(int, input().split(',')))
print(a, b)
a.sort()
b.sort()
# 在几个值中找最大值
minnums = []
# 中间位置的计算
for i in range(len(a)):
minnum = 10000
for j in range(len(b)):
if (abs(a[i] - b[j]) < minnum):
minnum = abs(a[i] - b[j])
minnums.append(minnum)
print(minnums)
result = max(minnums)
print(result)
3,租书屋的老板打算进一批新书,选出了n本价格相同的书作为备选,并给vip读者发了一批调查问卷,手机到每位读者想看的书的列表,为了节省采购成本,老板决定在办证每位vip读者至少有一本喜欢的书进入采购清单,最小化采购费用。输出最少的采购书本的数量
输入示例
5
3
1 2 3 4 5
1 2
2
3 4
原型题:动态规划+二分搜索
题目:
你将获得 K 个鸡蛋,并可以使用一栋从 1 到 N 共有 N 层楼的建筑。
每个蛋的功能都是一样的,如果一个蛋碎了,你就不能再把它掉下去。
你知道存在楼层 F ,满足 0 <= F <= N 任何从高于 F 的楼层落下的鸡蛋都会碎,从 F 楼层或比它低的楼层落下的鸡蛋都不会破。
每次移动,你可以取一个鸡蛋(如果你有完整的鸡蛋)并把它从任一楼层 X 扔下(满足 1 <= X <= N)。
你的目标是确切地知道 F 的值是多少。
无论 F 的初始值如何,你确定 F 的值的最小移动次数是多少?
示例:
输入:K = 1, N = 2
输出:2
解释:
鸡蛋从 1 楼掉落。如果它碎了,我们肯定知道 F = 0 。
否则,鸡蛋从 2 楼掉落。如果它碎了,我们肯定知道 F = 1 。
如果它没碎,那么我们肯定知道 F = 2 。
因此,在最坏的情况下我们需要移动 2 次以确定 F 是多少。
思路:鸡蛋数量和楼层确定后,最小移动次数也会确定,所以利用动态规划,获得k,n对应的次数。
1,将鸡蛋从楼层x扔下来有两种情况,碎和不碎,若碎则下一步f(k-1,x-1),不碎的话f(k,n-x),所以找到临界楼层需要的最小移动次数为f(k,n)=max{fx(k-1,x-1),fx(k,n-x)}
2,可知f(k,n)应该为所有情况的最小值,即f(k,n)=min{f1(),f2(),…fn()}
时间复杂度 :O(kn^2)
class Solution:
def superEggDrop(self, k: int, n: int) -> int:
def parse(k, n):
if n == 1: # 如果只有1层,不管有多少蛋只需试1次
return 1
elif n == 0:
return 0
elif k == 1: # 只有1个鸡蛋,则只能逐层试
return n
elif (k, n) in table:
return table[(k, n)]
f = float('inf') # 定义一个无限大数作为初始条件
for x in range(1, n + 1): # 将鸡蛋扔在第x层,从第1层开始
fx = 1 + max(parse(k - 1, x - 1), parse(k, n - x))
f = min(f, fx)
table[(k, n)] = f
return f
table = {} # 记忆被计算过的情况
return parse(k, n)
优化方案:动态规划+二分搜索
观察可知fx(k-1,x-1)随着x增加单调递增,fx(k,n-x)随着x增加单调递减,因此可以用二分查找找出最坏的情况。把找出最小值得代码:
for x in range(1, n + 1): # 将鸡蛋扔在第x层,从第1层开始
fx = 1 + max(parse(k - 1, x - 1), parse(k, n - x))
f = min(f, fx)
改为:
while lp <= rp:
mid = lp + (rp-lp) // 2 # 二分法优化
bcase = parse(k-1, mid-1) # 蛋碎的情况
notbcase = parse(k,n-mid) # 不碎的情况
# fx = 1 + max(bcase, notbcase)
if bcase > notbcase:
rp = mid - 1
f = min(f, bcase + 1)
else:
lp = mid + 1
f = min(f,notbcase + 1)
每日一题:
给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?请你找出所有满足条件且不重复的三元组。
思路:将数组排序,基准数字从头开始遍历,使用两个指针在后面的数字的首位进行遍历,因为数组是单调的,所以搜索具有单向性。
def threeSum(self,nums):
nums.sort()
res, k = [], 0
length = len(nums)
for k in range(length-2):
if nums[k] > 0: # 后面的都大于0,和肯定也都大于零
break
if(k>0 and nums[k]==nums[k-1]):#避免重复的三元组,下一个基准数和上一个一样,直接跳过
continue
i = k + 1
j = length - 1
while i < j:#两个首尾指针满足的条件
if((nums[k] + nums[i] + nums[j] )< 0):
i += 1
elif ((nums[k] + nums[i] + nums[j]) > 0):
j -= 1
else:
a = [nums[k], nums[i], nums[j]]
res.append(a)
while i<j:
if(nums[i] == nums[i + 1] ):#排除相同元组
i+=1
elif(nums[j] == nums[j - 1]):
j-=1
else:
i+=1
j-=1
break
return res
LeetCode 40.给定一个数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。
candidates 中的每个数字在每个组合中只能使用一次。
def combinationSum2(self, candidates: List[int], target: int) -> List[List[int]]:
candidates.sort()#现将数组排序,有序保证搜索方向的单调性
self.res = []
if (target == 0 or len(candidates) == 0):
return res
def helper(tar, cur, idx):
# cur记录当前记录的节点,所以要传下去
if (tar==0):
# 开始新的搜索
self.res.append(cur[:])
for i in range(int(idx), len(candidates)):
if (tar < candidates[i]):
# 后面的数肯定大于tar,不存在目标值,直接结束
break
elif (i > idx and candidates[i] == candidates[i - 1]):
# 避免重复的节点
continue
# 剩下的就是节点小于目标值,将节点保存,进行下一步的搜索
cur.append(candidates[i])
helper(tar - candidates[i], cur,i + 1)
cur.pop() # 将状态恢复,进一步的搜索
helper(target, [], 0)
return self.res
给定一个非空二叉树,返回其最大路径和。
本题中,路径被定义为一条从树中任意节点出发,达到任意节点的序列。该路径至少包含一个节点,且不一定经过根节点。
问题思路:
路径:二叉树的子节点由根节点连接,一个基本的二叉树的路径包括:左上,右上,左上右,三种方式,同时也可以只选择一个单节点。
1.三条路径意味着三指大小比较;
2,先用递归,比较出左上还是右上 ,同时使用全局最大值变量记录最大值路径,因为路径不一定包含根节点,还加上自己单值,比较出最大值;
3,得到最大值后,计算左上右的值,也是这种方式的最大值。通过比较再得到三值的最大值。
def maxPathSum(self, root: TreeNode) -> int:
self.max_path_sum = float('-inf')
def dfs(node):
if not node: # 边界情况
return 0
left = dfs(node.left) # 对左右节点dfs
right = dfs(node.right)
#三选二的问题,左中右,左上,右上,先使用递归选择左右两边,再比较和左中右的大小,同时因为节点可以不包括根节点,所以需要不断地更新全局最大的路径
#选择了左还是右,还是都不选
cur_max = max(
node.val,
node.val + left,
node.val + right,
)
# 更新全局变量 也是选下 cur_max和左中右的大小,同时更新全局最大值
self.max_path_sum = max(self.max_path_sum, cur_max, node.val + left + right)
return cur_max
dfs(root)
return self.max_path_sum
题目来源:Leetcode
给定两个大小为 m 和 n 的正序(从小到大)数组 nums1 和 nums2。
请你找出这两个正序数组的中位数,并且要求算法的时间复杂度为 O(log(m + n))。
你可以假设 nums1 和 nums2 不会同时为空。
def getKthElement(k):
"""
- 主要思路:要找到第 k (k>1) 小的元素,那么就取 pivot1 = nums1[k/2-1] 和 pivot2 = nums2[k/2-1] 进行比较
- 这里的 "/" 表示整除
- nums1 中小于等于 pivot1 的元素有 nums1[0 .. k/2-2] 共计 k/2-1 个
- nums2 中小于等于 pivot2 的元素有 nums2[0 .. k/2-2] 共计 k/2-1 个
- 取 pivot = min(pivot1, pivot2),两个数组中小于等于 pivot 的元素共计不会超过 (k/2-1) + (k/2-1) <= k-2 个
- 这样 pivot 本身最大也只能是第 k-1 小的元素
- 如果 pivot = pivot1,那么 nums1[0 .. k/2-1] 都不可能是第 k 小的元素。把这些元素全部 "删除",剩下的作为新的 nums1 数组
- 如果 pivot = pivot2,那么 nums2[0 .. k/2-1] 都不可能是第 k 小的元素。把这些元素全部 "删除",剩下的作为新的 nums2 数组
- 由于我们 "删除" 了一些元素(这些元素都比第 k 小的元素要小),因此需要修改 k 的值,减去删除的数的个数
"""
index1, index2 = 0, 0
while True:
# 特殊情况
if index1 == m:
return nums2[index2 + k - 1]
if index2 == n:
return nums1[index1 + k - 1]
if k == 1:
return min(nums1[index1], nums2[index2])
# 正常情况
newIndex1 = min(index1 + k // 2 - 1, m - 1)
newIndex2 = min(index2 + k // 2 - 1, n - 1)
pivot1, pivot2 = nums1[newIndex1], nums2[newIndex2]
if pivot1 <= pivot2:
k -= newIndex1 - index1 + 1
index1 = newIndex1 + 1
else:
k -= newIndex2 - index2 + 1
index2 = newIndex2 + 1
m, n = len(nums1), len(nums2)
totalLength = m + n
if totalLength % 2 == 1:
return getKthElement((totalLength + 1) // 2)
else:
return (getKthElement(totalLength // 2) + getKthElement(totalLength // 2 + 1)) / 2