2020-02算法刷题集
-
(0201)-(救生艇)
第
i
个人的体重为people[i]
,每艘船可以承载的最大重量为limit
。每艘船最多可同时载两人,但条件是这些人的重量之和最多为
limit
。返回载到每一个人所需的最小船数。(保证每个人都能被船载)。
注意:1≤people.length≤50000,1≤people[i]≤limit≤30000
题目来源:领扣LintCode—1061救生艇
解题思路:可以见2020-01算法刷题集,参考题目0113巨人排队和0131会议室Ⅱ的处理流程
将所有人按照体重进行降序排列,遍历列表,实现将每一个人按照顺序进入救生艇的过程
首先创建一个救生艇列表,将救生艇的最大载重量减去当前救生艇的承重量,为剩余的载重量
当剩余载重量满足下一个人进入时,则重复以上动作
当剩余的载重量不满足下一个人进入时,则将救生艇的最大载重量减去当前救生艇的承重量再次探入救生艇列表中
列表的长度即是最少船数。
程序代码:
#0202.py-救生艇
def findindex(list):
for i in range(len(list)):
if(list[i]==1):
return i
return -1
def trueorfalse(ele,list):
tf_list = [0 for i in range(len(list))]
for i in range(len(list)):
if(list[i]>=ele):
tf_list[i] = 1
return tf_list
def run(limit,peopleList):
shipList = []
for i in range(len(peopleList)):
tf_list = trueorfalse(peopleList[i],shipList)
if(findindex(tf_list)==-1):
shipList.append(limit-peopleList[i])
else:
shipList[findindex(tf_list)]=shipList[findindex(tf_list)]-peopleList[i]
return shipList
def main():
limit = eval(input())
inputList = input().split(",")
people = []
for i in inputList:
people.append(int(i))
people.sort(reverse=True)
shipList = run(limit,people)
print(len(shipList))
main()
运行结果:
-
(0204)-(木材加工)
一些原木,现在想把这些木头切割成一些长度相同的小段木头,需要得到的小段的数目至少为
k
。当然,我们希望得到的小段越长越好,你需要计算能够得到的小段木头的最大长度。注意:
木头长度的单位是厘米。原木的长度都是正整数,我们要求切割得到的小段木头的长度也要求是整数。无法切出要求至少 k 段的,则返回
0
即可。input:
232,124,456
output:
114
题目来源:领扣LintCode—183木材加工
解题思路:
首先,这道题可以通过暴力解法,假设最大长度为1,通过不断增加到达一定数值后,不再满足条件时,那么该数值就是最大长度。
这里用的是二分法解决的,类似于之前的二分查找,相关代码在以下的附带有给出。
二分法的关键是范围,这道题的范围是0-最长木材的长度。因为最终答案的小段木头的最大长度肯定小于所有木头中最长的木头长度。
另外,这里做了两处优化:
优化1,当所有木头的总长度小于得到的小段木头的数目k时,是无法进行算法的。因为每一小段木头的长度只能是整数,即最小的长度是1
优化2,二分法的效率是高,但有时可能会出现很小的偏差,例如答案是114,那么会出现113,或者112。所以需要将二分函数处理得到的答案再不断加1,当不满足条件时,再停止验证,直接输出。尽可能得到更大的答案。
程序代码:
#0204-木材加工
def wood(list,k,start,end):
count = 0
length = int((end-start)/2)+start
for i in list:
count +=int(i/length)
if(count==k):
return length
elif(count<k):
return wood(list,k,start,length-1)
else:
return wood(list,k,length+1,end)
return wood(list,k,start,end)
def beauty(length,list,k):
count = 0
for i in list:
count += int(i/length)
if(count==k):
return True
else:
return False
def main():
L = []
maxLength = 0
inputList = input().split(",")
k = eval(input())
ListSum = 0
for i in inputList:
ListSum +=int(i)
L.append(int(i))
if(maxLength<int(i)):
maxLength = int(i)
if(int(ListSum/k)==0):
print(0)
else:
length = wood(L,k,0,maxLength)
while(beauty(length+1,L,k)):
length +=1
print(length)
main()
结果截图:
附带:二分查找算法(递归和非递归两个版本)全部代码
#二分查找
#非递归版本
def binarySearch1(list,k):
low = 0
high = len(list)
while(low<high):
mid = (high-low)/2+low
if(list[mid]==k):
return mid
elif(list[mid]>k):
high = mid-1
else:
low = mid+1
return -1
def binarySearch2(list,low,high,k):
if(low>high):
return -1
mid = (high-low)/2+low
if(list[mid]>k):
return binarySearch2(list,low,mid-1,k)
elif(list[mid]<k):
return binarySearch2(list,mid+1,high,k)
else:
return mid
return binarySearch2(list,low,high,k)
def main():
list = [1,2,3,4,6,8]
k = 4
print("----使用非递归方式进行二分查找:----")
if(binarySearch1(list,k)==-1):
print("查找失败")
else:
print("查找成功,待查找元素在{}位置".format(binarySearch1(list,k)+1))
print("----使用递归方法进行二分查找:----")
if(binarySearch2(list,0,len(list)-1,4)==-1):
print("查找失败")
else:
print("查找成功,待查找元素在{}位置".format(binarySearch2(list,0,len(list)-1,4)+1))
main()
-
(0206)-(尾部的零)
设计一个算法,计算出n阶乘中尾部零的个数
示例:
input:
11
output:
2
解释:11!=39916800,结尾的0有2个
背景:由于日程安排完成顺利,有时间,所以刷了一道简单的题。
题目来源:领扣LintCode—2尾部的零
解题思路:
思路1:通过求得阶乘的最终结果,进行判断。
思路2:0的产生是由于阶乘中10的出现,而10或者10的倍数的出现,再者说是因为5和偶数的出现。
因为阶乘中偶数的数量很多,所以最终的决定因素在于5的数量。所以判断阶乘中5或者5的倍数或者5的幂出现的次数即可。
另外需要注意的是5的幂,例如25,需要将25看成是5*5,即两个5,会产生2个0,而非一个5。
程序代码:
思路1:
#LintCode002-尾部零
def factorial(n):
if(n==1):
return n
else:
return n*factorial(n-1)
def findzeronum(num):
numstr = str(num)
count = 0
for i in range(1,len(numstr)):
if(numstr[-i]=='0'):
count += 1
else:
break
return count
def main():
num = eval(input(""))
sum = factorial(num)
count = findzeronum(sum)
print(count)
main()
思路2:
class Solution:
"""
@param: n: An integer
@return: An integer, denote the number of trailing zeros in n!
"""
def trailingZeros(self, n):
# write your code here, try to do it without arithmetic operatorsself.
count = 0
while(n):
count = count+n//5
n = n//5
return count
if __name__ == '__main__':
solution = Solution()
num = eval(input(""))
print(solution.trailingZeros(num))
运行结果:
-
(0207)-(统计数字)
计算数字 k 在 0 到 n 中的出现的次数,k 可能是 0~9 的一个值。
input:
k = 1 n = 12
output:
5
解释:1在0~12中出现5次,分别是1,10,11,12
题目来源:领扣LintCode—3统计数字
解题思路:
有点像之前做过的计算书本页码中所有数字出现的 次数问题
由于Python的字符串直接,所以直接暴力破解。
以后有空余的时间,将会使用更优化的方案解决,并在这里补充
程序代码:
#统计数字
def findnum(n,k):
K_str = str(k)
n +=1
count = 0
for i in range(n):
for j in range(len(str(i))):
i_str = str(i)
if(i_str[j] == K_str):
count+=1
return count
def main():
k = eval(input(""))
n = eval(input(""))
count = findnum(n,k)
print(count)
main()
结果截图:
-
(0210)-(全排列)
给定一个数字列表,返回其所有可能的排列。
input:
1,2,3
output:
[[1, 2, 3], [1, 3, 2], [2, 1, 3], [2, 3, 1], [3, 2, 1], [3, 1, 2]]
题目来源:LintCode-15全排列
解题思路:没啥好讲的,之前做过了,直接使用模板即可
程序代码:
#0210全排列
per_result = []
def per(lst,s,e):
if s == e:
per_result.append(list(lst))
else:
for i in range(s,e):
lst[i],lst[s] = lst[s],lst[i]#试探
per(lst,s+1,e)#递归
lst[i],lst[s] = lst[s],lst[i]#回溯
def main():
inputList = input("").split(",")
numsList = []
for i in list(inputList):
numsList.append(int(i))
per(numsList,0,len(numsList))
print(per_result)
main()
结果截图:
-
(0213)-(背包问题)
在n个物品中挑选若干物品装入背包,最多能装多满?假设背包的大小为m,每个物品的大小为A[i]
注意:你不可以将物品进行切割。
input:
3,4,5,8
10
output:
9
题目来源:LintCode领扣-92背包问题
解题思路:
这道背包问题,根据题意知道是01背包问题,与以前的01背包问题有所区别的是,最优结果是物品的重量,而不是价值
刚开始想要通过暴力直接破解,但发现有很多细节是无法处理的,所以改变思路,选择了动态规划。
由于对动态规划的认识很浅,所以解决这类问题的时候,常常会构造一个建图函数,然后再一边填充一边测试数据是否与需求一致。
—这里的建图函数是:buidmap(list,size)
横轴是0~背包的容量
竖轴是物品的数量
—打印函数是:printMap(map,list,size)
实现效果如上
—然后就是处理过程了:
——其中在建图函数中,有一个处理,就是填充第一行的数据,
只要背包的容量>=第一件物品的重量,
那么该位置及其右边的位置全部填充为第一件物品的重量,这里是3,如下:
(0222—这里修改以下,第一行是不需要填充的,因为第一行的数据可以通过图最后一行得到的
例如当i=0时,map[i-1][j]=map[-1][j]即图最后一行,直接看下面内容即可
)
——根据以下公式即可完成填充:
选True还是不选False
注意:这里wi指的是物品i的重量
—最后,将图的最后一行的最后一个元素返回即可
题外话:在LintCode上只通过了一半的数据,另外一半数据未能通过,原因是时间复杂度过大。
以后有时间,会想办法提高效率。
程序代码:
#0213-背包问题
def printMap(map,list,size):
print(" ",end=":")
sizeList = [i for i in range(size)]
print(sizeList)
for i in range(len(map)):
print(list[i],end=" ")
print(i,end=":")
print(map[i])
def getMax(a,b):
if(a>b):
return a
else:
return b
def buidmap(list,size):
lenoflist = len(list)
map = []
for i in range(len(list)):
ele = [0 for i in range(size)]
map.append(ele)
# for i in range(len(map[0])):
# if(i>=list[0]):
# map[0][i]=list[0]
return map
def dynamicprogramming(map,list,size):
for i in range(len(list)):
for j in range(1,size):
if(list[i]>j):
map[i][j]=map[i-1][j]
else:
map[i][j] = max(map[i-1][j],map[i-1][j-list[i]]+list[i])
return map
def main():
inputList = input("").split(",")
goodsList = []
for i in inputList:
goodsList.append(int(i))
size = eval(input(""))
size+=1
map = buidmap(goodsList,size)
resultmap = dynamicprogramming(map,goodsList,size)
# print("结果:")
printMap(resultmap,goodsList,size)
print(resultmap[-1][-1])
main()
运行结果:
-
(0216)-(爬楼梯)
假设你正在爬楼梯,需要n步你才能到达顶部。但每次你只能爬一步或者两步,你能有多少种不同的方法爬到楼顶部?
题目来源:LintCode领扣-111爬楼梯
解题思路:
首先,分析一下,1级台阶、2级、3级台阶应该有多少种方案:
另外根据题意,有个关键的信息,就是第n级台阶是从n-2级台阶和n-1级台阶爬上去的。
所以很容易得到,爬n级台阶的方案等于n-2级台阶和n-1级台阶的方案之和
通过上面的例子,拿4级台阶验证一下:
可以发现,4级台阶的方案=3级台阶的方案+2级台阶的方案
所以有以下公式:
即斐波那契数列,直接套用模板即可
程序代码:
#0216爬楼梯
#递归版
def fibonacci(n):
if(n==1):
return 1
elif(n==2):
return 2
elif(n==0):
return 0
else:
return fibonacci(n-1)+fibonacci(n-2)
def main():
n = eval(input())
num = fibonacci(n)
print(num)
main()
#非递归版
class Solution:
"""
@param n: An integer
@return: An integer
"""
def climbStairs(self, n):
# write your code here
if(n==0):
return 0
elif(n==1):
return 1
elif(n==2):
return 2
else:
n+=1
a = [0 for i in range(n)]
a[1]=1
a[2]=2
for i in range(3,n):
a[i]=a[i-1]+a[i-2]
return a[-1]
运行结果:
-
(0219)-(背包问题Ⅱ)
有
n
个物品和一个大小为m
的背包. 给定数组A
表示每个物品的大小和数组V
表示每个物品的价值.问最多能装入背包的总价值是多大?
注意:
A[i], V[i], n, m
均为整数- 你不能将物品进行切分
- 你所挑选的要装入背包的物品的总大小不能超过
m
- 每个物品只能取一次
示例1:
输入:
m=10
A = [2,3,5,7]
V = [1,5,2,4]
输出:
9
示例2:
输入:
m=10
A = [2,3,8]
V = [2,5,8]
输出:
10
题目来源:LintCode领扣—125背包问题Ⅱ
解题思路:没啥好说的,还是之前的套路,见0213
程序代码:
#背包问题
def printMap(map,weightList,valueList,size):
print("W V n",end=":")
sizeList = [i for i in range(size)]
print(sizeList)
for i in range(len(map)):
print(weightList[i],end=" ")
print(valueList[i],end=" ")
print(i,end=":")
print(map[i])
def buidmap(wlist,vlist,size):
lenoflist = len(wlist)
map = []
for i in range(lenoflist):
ele = [0 for i in range(size)]
map.append(ele)
#将第一件物品装入背包,并填入价值
for i in range(len(map[0])):
if(i>=wlist[0]):
map[0][i]=vlist[0]
return map
def dynamicprogramming(map,wlist,vlist,size):
lenoflist = len(vlist)
for i in range(lenoflist):#竖轴
for j in range(1,size):#横轴
#选
if(j>=wlist[i]):
map[i][j]=max(map[i-1][j],map[i-1][j-wlist[i]]+vlist[i])
#不选
else:
map[i][j]=map[i-1][j]
return map
def main():
inputofwlist = input("").split(",")
inputofvlist = input("").split(",")
size = eval(input(""))
size+=1
valueList = []
weightList = []
for i in inputofvlist:
valueList.append(int(i))
for i in inputofwlist:
weightList.append(int(i))
map = buidmap(weightList,valueList,size)
#printMap(map,weightList,valueList,size)
resultMap = dynamicprogramming(map,weightList,valueList,size)
#printMap(resultMap,weightList,valueList,size)
print(resultMap[-1][-1])
main()
结果截图:
-
(0222)—(背包问题Ⅳ)
给出 n 个物品, 以及一个数组,
nums[i]
代表第i个物品的大小, 保证大小均为正数并且没有重复, 正整数target
表示背包的大小, 找到能填满背包的方案数。每一个物品可以使用无数次
示例1:
nums = [2,3,6,7]
target = 7
解释:
方案有 [7],[2,2,3]
题目来源:LintCode-562背包问题
解题思路:
由于没看清楚要求,刚开始时的思路是直接写出背包问题模板,最终得到答案的是背包的容量。
发现与答案有区别。所以重新解题。
借鉴了一位博主的思路
https://blog.csdn.net/qq_xuanshuang/article/details/104023730
在演算过程中,发现了另外一种解法。关于复杂度问题,个人觉得是优化了的,但LintCode的智能判题系统却反应复杂度更高。这里保留问题。
以下是个人的另外一种解法:
对比正常的动态规划,有什么改进呢?
首先,不需要图,图的复杂度常常是双层for循环,即n*m的复杂度
因为通过以上公式,可以省去许多没必要计算的项,比如dp[1][4],dp[2][3]等等,是不需要计算的
至于答案,只要根据以下这个列表即可查询的出来
这里有0,1的区别,只要(0~7)%nums[0]==0的则填充为1,其它为0
所以dp[3][7]=dp[0][7]+dp[0][4]+dp[0][1]+dp[0][3]+dp[0][0]+dp[0][0]=3
实现过程,使用了元组记录坐标,列表记录元组
程序代码:
#0222-背包问题
def backPackIV(nums,result):
spareResult = []
lenofnums = len(nums)-1#3
for i in range(lenofnums):
for j in range(len(result)):
t = result.pop(0)
time = t[1]//nums[-i-1]
for time in range(time+1):
tuple = (t[0]-1,t[1]-time*nums[-i-1])
spareResult.append(tuple)
result = spareResult
spareResult = []#初始化备用列表
return result
def count(result,list):
count = 0
for i in result:
count += list[i[1]]
return count
def main():
nums = []
inputnums = input().split(",")
for i in inputnums:
nums.append(int(i))
target = eval(input())
result = [(len(nums)-1,target)]
result = backPackIV(nums,result)
lst = [ 0 for i in range(target+1)]
for i in range(len(lst)):
if(i%nums[0]==0):
lst[i]=1
counts = count(result,lst)
print(counts)
main()
洗澡过程中,想了一下,有许多变量是对结果没有影响的,所以删去。这样更加简便,处理过程更加清晰.
解题思路与上面大致相同,不同的是处理过程:
上面是从底向上,这里是
将物品列表的最后一个元素弹出并保存。
将target存入结果列表中
函数run部分:
遍历物品列表,并从依次结果列表取出target与物品重量作差,结果存入备用列表中
再将备用列表赋值给结果列表
当物品列表遍历完成,即获得最终结果列表
函数count部分:与上面的相同
代码如下,运行结果是一样的
#0222
def run(nums,result):
spareResult = []
for i in nums:
for j in range(len(result)):
target = result.pop(0)
while(target>=0):
spareResult.append(target)
target -= i
result = spareResult
spareResult = []#初始化备用列表
return result
def count(result,list):
count = 0
for i in result:
count += list[i]
return count
def main():
nums = []
inputnums = input().split(",")
for i in inputnums:
nums.append(int(i))
target = eval(input())
# nums = [2,3,6,7]
# target = 7
size = nums.pop(-1)
targetList = [0 for i in range(target+1)]
for i in range(target+1):
if(i%size==0):
targetList[i]=1
result = [target]
result = run(nums,result)
counts = count(result,targetList)
print(counts)
main()
运行结果:
-
(0225)-(斐波那契数列Ⅱ)
在斐波那契数列中,F0 = 0, F1 = 1, 和 Fn = Fn − 1 + Fn − 2 对于 n ≥ 2。举一个例子,斐波那契数列中前十项为: 0,1,1,2,3,5,8,13,21,34。
示例1:
input:
9
output:
34
题目来源:LintCode领扣-946斐波那契数列Ⅱ
解题思路:编辑斐波那契函数,直接根据项求出结果,再根据结果获得最后4位数
程序代码:
#0225
#斐波那契数列Ⅱ
def fibonacci(n):
if(n==0):
return 0
elif (n==1):
return 1
else:
return fibonacci(n-1)+fibonacci(n-2)
def getlastfournum(n):
if(len(str(n))>4):
return int(str(n)[-4:])
else:
return n
def main():
n = eval(input())
num =fibonacci(n)
result = getlastfournum(num)
print(result)
main()
运行结果:
-
(0228)-(约翰的后花园)
约翰想在他家后面的空地上建一个后花园,现在有两种砖,一种
3
dm的高度,7
dm的高度。约翰想围成x
dm的墙。如果约翰能做到,输出YES,否则输出NO。注意:X是一个整数,取值范围为
[3, 1000]
示例:
input:10
output:YES
reason:10=7+3
题目来源:LintCode-749. 约翰的后花园
解题思路:首先想到的是暴力拆解,但突然看到一个结论就是3-1000中,只有4,5,8,11不符合条件。
所以先通过暴力破解进行验证,大概验证思路是:暴力将符合的i装入列表,并消除相同元素后,返回列表的长度
如果列表的长度+不符合条件的元素=1000-3+1,则说明这个思路是对的,否则是错的。
以下是验证代码:
#验证
#将符合的i装入列表,并返回
def run():
result = []
for i in range(3,1001):
#1000/3 = 333
#1000/7 = 142
#142*7 = 994
for x in range(0,335):
for y in range(0,144):
if(3*x+7*y==i):
result.append(i)
break
return result
#消除相同的元素,得到所有且唯一满足条件的元素
def rmsame(list):
result = []
result.append(list[0])
for i in range(1,len(list)):
if(result[-1]!=list[i]):
result.append(list[i])
return result
def main():
ls = run()
finaResult = rmsame(ls)
print(finaResult)
#1-11中有 [4,5,8,11]有4个不符合条件
print(len(finaResult))#返回994
#4+994=1000-3+1
#验证成功
main()
通过以上代码可以知道这个解题思路是对的。
程序代码:
#0228
#约翰的后花园
def run(n):
if(n>11):
return "YES"
else:
ls = [4,5,8,11]
if(n in ls):
return "NO"
else:
return "YES"
def main():
print(run(11))
main()
运行结果:
总结:
由于这几天忙着一些事情,所以到现在才完成2月份的刷题集。3天一篇的确可以给自己留出很多时间,但刷题集的总结并没有更加详细,完成进度基本不会被搁置。另外这一个月份刷的题的难度与1月份相比,难度降低了。
3月份会继续,会更改为2天一篇,因为开学的原因以及事情也会没那么多了。