# 接下来就是算法基础内容中最重要的贪心算法和暴力递归了
#
# 那么什么是贪心算法呢?
# 在某一标准下,优先考虑最满足标准的样本,最后考虑最不满足标准的样本,最终得到一个答案的算法,叫做贪心算法。
# 也就是说,不从整体最优上加以考虑,所作出的是某种意义上的局部最优解
# 先拿一个经典的会议安排问题开头吧
# 一些项目要占用一个会议室宣讲,会议室不能同时容纳两个项目的宣讲。[开始时间,结束时间]
# 给你每一个项目开始的时间和结束的时间,你来安排宣讲的日程,要求会议室进行的宣讲场次最多,返回宣讲场次
def arrange_program(programs):
programs = sorted(programs, key=lambda x: (x[1], x[0]), reverse=False)
start = 6
res = []
count = 0
while programs != []:
for index in range(len(programs)):
program = programs[index]
if start <= program[0]:
start = program[1]
res.append(program)
count += 1
while True:
index += 1
if index >= len(programs):
return res, count
if programs[index][0] >= start:
programs = programs[index:]
break
break
return res, count
# 分金条
# 一块金条切成两半,需要花费和长度一样的铜板。比如长度为20的金条,不管切成长度多大的两半都要花费20铜板。问:怎样分最省?
# 例:给一个数组[10,20,30],代表三个人,金条总长度为60
# 解题思路:怎么分花费最少,换个方向来看,怎么把数组里的数合成的时候合成的值最小
def divide_money(arr):
res = 0
while len(arr) > 1:
arr.sort()
a, b = arr.pop(0), arr.pop(0)
money = a + b
res += money
arr.append(money)
return res
# costs[i]代表i好项目的花费,profits[i]代表i号项目的利润,k代表最多能做多少个项目,m表示你的初始资金
# 没做完一个项目,马上能获得收益。输出最后能获得的最大钱数
# 解题思路:在能做的项目中优先做利润最高的
def most_money(costs, profits, k, m):
programs = []
for i in range(len(costs)):
programs.append([costs[i], profits[i]])
# programs=[[花费,利润]],按照利润由高到低排序,利润相同按照花费由小到大排序
programs = sorted(programs, key=lambda x: (x[1], -x[0]), reverse=True)
while k != 0 and len(programs) != 0:
for index in range(len(programs)):
program = programs[index]
if m >= program[0]:
m += program[1]
programs.pop(index)
k -= 1
break
if index == len(programs) - 1:
return m
return m
# 在一个数据流中,随时取得中位数
# 遇到这种"随时"的题目,第一时间应该想到堆结构
# 解题思路:想要得到一组数的中位数,只需要把较小的一半放到大根堆里,较大的一半放到小根堆里,需要d出的时候返回拥有较多数据的那个堆的堆顶就行
# 这里用列表简单代替堆结构,详细的堆结构请看排序章节,用堆的时间复杂度可以将为logN,用列表则是N
def get_median():
small_heap = []
big_heap = []
while True:
num = input()
if num.isdigit() == False:
return
num = int(num)
if big_heap == []:
big_heap.append(num)
else:
if num >= big_heap[0]:
small_heap.append(num)
else:
big_heap.append(num)
big_heap = sorted(big_heap, reverse=True)
small_heap = sorted(small_heap, reverse=False)
if len(big_heap) - len(small_heap) >= 2:
small_heap.append(big_heap.pop(0))
elif len(small_heap) - len(big_heap) >= 2:
big_heap.append(small_heap.pop(0))
print(big_heap, small_heap)
if len(big_heap) > len(small_heap):
print(big_heap[0])
elif len(big_heap) < len(small_heap):
print(small_heap[0])
else:
print((small_heap[0] + big_heap[0]) / 2)
'''
下面是暴力递归内容
其实暴力递归就是尝试:
1.把问题转化为规模缩小的同类问题的子问题
2.有明确的不需要继续进行递归的条件(base case)
3.有当得到子问题结果后的决策过程
4.不记录每一个子问题的解
'''
# n层汉诺塔问题
# 左中右三个杆子,三个杆子都可以相互移动圆盘,最左边有n层从大到小的圆盘,返回把最左边的圆盘全部移到最右边所需的步数,且全程不能让大圆盘压住小圆盘
# 解题思路:
# 要移动n层圆盘而且不能让大圆盘压住小圆盘,那么肯定是要先把最下面的移动到end.
# 要把最下面的圆盘移到end,就要先把n-1个移动到other
# 把最下面的圆盘移动到end之后,其他圆盘现在在other,要做的就是把n-1个圆盘移动到end上
def hanoi_tower1(n, start, end, other):
if n == 1:
print("form" + start + "to" + end)
return
hanoi_tower1(n - 1, start, other, end)
hanoi_tower1(1, start, end, other)
hanoi_tower1(n - 1, other, end, start)
# 打印一个字符串的全部子序列,包括空字符
def all_subsequence_str(chr, i, res, ans):
if i == len(chr):
ans.append(res)
return
all_subsequence_str(chr, i + 1, res + chr[i], ans)
all_subsequence_str(chr, i + 1, res, ans)
return ans
# 打印一个字符串的全部排列
# 要求不要出现重复排列
# 不出现重复排列的方法:
# 1.res使用set()类型
# 2.在交换前先检查曾经有没有出现过(下面使用的就是这种,这样更快)
def all_permutation_str(chr, i, res):
chr = list(chr)
if i == len(chr):
res.append(chr)
return
isVisit = []
for index in range(i, len(chr)):
if chr[index] not in isVisit:
isVisit.append(chr[index])
chr[index], chr[i] = chr[i], chr[index]
all_permutation_str(chr, i + 1, res)
chr[index], chr[i] = chr[i], chr[index]
return res
# 给定一个整型数组arr,代表数值不同的纸牌排成一条线.玩家A和玩家B依次拿走每张纸牌,规定A先拿,B后拿.但是每个玩家只能拿走最左或者最右的纸牌.请返回获胜者的分数
# 解题思路:首先要弄清楚在[L,R]上先手的人,因为抽掉了一次,所以在[L,R-1]或[L+1,R]上是后手的,所以把先后手其实可以理解为交替取
# 所以,当前取的时候,当然要让自己取的+在[L,R-1]或[L+1,R]上取得最大
# 在[L,R]上后取的时候,实际是在[L,R-1]或[L+1,R]上先取,但是哪个能取得更多并不是由后手的人决定的,所以要在这两个之间取最小值,来保证当前在取的值最大
def now_get(arr, L, R):
if L == R:
return arr[L]
return max(next_get(arr, L, R - 1) + arr[R], next_get(arr, L + 1, R) + arr[L])
def next_get(arr, L, R):
if L == R:
return 0
return min(now_get(arr, L, R - 1), now_get(arr, L + 1, R))
def whoiswinner(arr):
return max(now_get(arr, 0, len(arr) - 1), next_get(arr, 0, len(arr) - 1))
# 给你一个栈,请你逆序这个栈,不能申请其他的数据结构
# 解题思路:1.想要逆序一个栈,首先要得到栈底的值.2.回溯,依次放入栈底
def get_bottom(stack):
# 每次执行递归都先保存了栈顶
res = stack.pop()
if stack == []:
return res
bottom = get_bottom(stack)
# 如果栈为空了就说明开始弹出的栈顶是原栈的栈底,沿路返回,把其他栈顶押回
stack.append(res)
# 两个return一个保证返回的是栈底,一个保证沿路返回,不会由其他参数干扰
return bottom
def reverse_stack(stack):
# 依次弹出栈底,直到栈为空返回,再把栈底压回
# [3,2,1]依次弹出3,2,1,为空之后回溯压入1,2,3
if stack == []:
return
value = get_bottom(stack)
reverse_stack(stack)
stack.append(value)
return stack
# 规定1和A对应、2和B对应...
# 那么一个数字字符可以有多少种转化结果
def conver_numTOstring(chr, i):
if i == len(chr):
return 1
if chr[i] == "0":
return 0
if chr[i] == "1":
res = conver_numTOstring(chr, i + 1)
if i < len(chr) - 1:
res += conver_numTOstring(chr, i + 2)
return res
if chr[i] == "2":
res = conver_numTOstring(chr, i + 1)
if i < len(chr) - 1 and chr[i + 1] <= "6":
res += conver_numTOstring(chr, i + 2)
return res
return conver_numTOstring(chr, i + 1)
# 背包问题
# 给定两个数组weights和values,分别代表物品的重量和价值.给定一个载重为bag的袋子,返回能装下最多的价值
# 解题思路:对每一个物品都做取舍
def knapsack(weights, values, bag, i):
if bag < 0:
return -1
if i == len(weights):
return 0
taken = knapsack(weights, values, bag - weights[i], i + 1)
if taken != -1:
taken = taken + values[i]
untaken = knapsack(weights, values, bag, i + 1)
return max(taken, untaken)
# N皇后问题
# 在N*N的棋盘上摆上N个皇后,要求任意两个皇后之间不同行、不同列、不同斜线,给定一个n,返回有多少种摆法
# 解题思路:深度遍历查看所有的摆法,如果可行就+1.用record记录每一行摆在哪一列,
# record[x]=y 在第x行摆在y列, 如果j==y,说明同列;如果abs(i-x)==abs(j-y),说明同斜线.
# 优化方法:用位运算替代record确定位置有效性
def nQueens(record, i, n):
if n == i:
return 1
res = 0
for j in range(n):
if isValid(record, i, j):
record[i] = j
res += nQueens(record, i + 1, n)
return res
def isValid(record, i, j):
for k in range(i):
if j == record[k] or abs(record[k] - j) == abs(i - k):
return False
return True
'''
总结:
1.贪心算法要从最小的问题取思考解法,找到满足局部最优的方法去尝试
2.暴力递归要站在宏观的角度去看整个问题
'''
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)