现在有x张十元纸币,y张五元纸币,z张两元纸币,购物后要支付n元(x,y,z,n为整数)。
要求编写一个复杂度为O(1)的函数FindSolution(x,y,z,n),
功能是计算出能否用现在手上拥有的纸币是否足够并能刚好凑齐n元,
而不需要找零。输出一种方案即可结束程序。
按n为奇还是偶来讨论
1.为偶,则5快的可要可不要(要的话一定是偶数张凑整十);直接看10块的够不够用
1.1 10块的够用(n//10 < x),不需要五块的(five_num=0);十块的数量可求(因为只要一种凑齐即可,10块按尽可能多给,简单处理:ten_num=n//10);接下来剩下来的个位数交给2块来凑(temp=n-10*ten_num),判断 temp//2的结果与z比较,若z为小,则2块不够,输出false;若z为大或者等,则two_num=temp//2,由此可得该分支下可能凑得齐的结果。
1.2 10块的不够用,先拿五全部来凑;
1.2.1 够凑的话拿偶数五凑满整十部分。个位数交给2来凑,同1.1判断,2够个位数就能输出,不够就false
1.2.2 不够凑的话,拿五块的最多的偶数张凑,剩下的2来凑。2凑完整十接着凑个位数,判断剩下的2是否可凑,不够就false
(易错点在于补5的时候,一定要控制补偶数张,因此要判断)
2.为奇,则五块必须要,且必须设置为奇数张,因此总数减1*5必为偶数,可以重新调用偶数函数
先排除特殊情况:
总数为1,3凑不出;5块为0凑不出;剩下进入正常讨论:
将num-5得到一个偶数,在次调用为偶的判断函数
res = func_double(x, y-1, z,num-5)
if res:
a, b, c = res
return a,b+1,c (补上5去奇变偶的那一张)
else:
return false
def n_double(x, y, z, n):
if n // 10 <= x:
ten_num = n // 10
five_num = 0
temp = n - ten_num * 10
if temp // 2 <= z:
two_num = temp // 2
return ten_num, five_num, two_num
else:
return False
else:
ten_num = x
temp = n - 10 * ten_num
if temp // 5 <= y:
if temp // 5 % 2 == 0:
five_num = temp // 5
else:
five_num = temp //5 - 1
else:
if y % 2 == 0:
five_num = y
else:
five_num = y - 1
temp_2 = temp - 5 * five_num
if temp_2 // 2 <= z:
two_num = temp_2 // 2
return ten_num, five_num, two_num
else:
return False
def n_single(x, y, z, n):
if n <= 3 or y < 1:
return False
else:
tempn = n - 5
if n_double(x, y, z, tempn):
ten_num, five_num, two_num = n_double(x, y, z, tempn)
five_num += 1
return ten_num, five_num, two_num
else:
return False
def find_solution(x, y, z, n):
if n % 2 == 0:
return n_double(x, y, z, n)
else:
return n_single(x, y, z, n)
if __name__ == "__main__":
print(find_solution(10, 20, 30, 87))
print(find_solution(6, 20, 30, 81))
print(find_solution(6, 2, 30, 87))
print(find_solution(6, 2, 10, 87))
给定一个整数数组和指定的数字和,求数组中相加等于指定和的所有子集,如:
输入: array = [2, 3, 7, 4, 10, 8, 6], sum = 10
输出: [2, 8], [3, 7], [4, 6], [10]
解题思路
递归:不停把需要考虑的范围一步步缩小 难点:数据格式调通
后期优化:@functools.lru_cache (节省递归操作时占用空间的工具,需要 import functools)
解答
import time
import functools
def calsumset(sum_num, lst):
"""
该函数用来输出给定整数和指定整数数组,指定中数组中元素相加等于指定和的子集的集合
:param sum_num:指定整数
:param lst:指定整数数组
:return:包含所有结果子集的集合
"""
result = []
for i, num in enumerate(lst):
if num == sum_num:
result.append([num])
continue
elif i == len(lst) - 1:
break
else:
tmp_set = calsumset(sum_num - num, lst[i+1:])
if tmp_set:
for k in tmp_set:
tmp = [num]
tmp.extend(k)
result.append(tmp)
return result
@functools.lru_cache(maxsize = 512)
def calsumset2(sum_num, lst):
"""
该函数用来输出给定整数和指定整数数组,指定中数组中元素相加等于指定和的子集的集合
:param sum_num:指定整数
:param lst:指定整数数组
:return:包含所有结果子集的集合
"""
result = []
for i, num in enumerate(lst):
if num == sum_num:
result.append([num])
continue
elif i == len(lst) - 1:
break
else:
tmp_set = calsumset(sum_num - num, lst[i+1:])
if tmp_set:
for k in tmp_set:
tmp = [num]
tmp.extend(k)
result.append(tmp)
return result
if __name__ == "__main__":
sum_num = 10
lst = [1,2 ,3, 4, 5, 6, 7, 9, 10]
t1 = time.time()
for i in range(100):
ret1 = calsumset(sum_num, lst)
t2 = time.time()
for i in range(100):
ret2 = calsumset2(sum_num, tuple(lst))
t3 = time.time()
print('origin need time: %s , upgrade need time: %s' % ((t2 - t1), (t3 - t2)) )
print('输出结果为:', ret2)
求当前日期过指定季数之后的季末日期
import math
def get_quarter_date(date_str, step_len):
"""
取当前日期过指定季数之后的季末日期
:param date_str: string, such as "20190212"
:param step_len: int
:return: string, like "20200301"
"""
days_sum = [31, 30, 30, 31]
if 8 != len(date_str) or not isinstance(step_len, int):
print("date_str or step_len error")
return
year_ori = int(date_str[0:4])
month_ori = int(date_str[4:6])
month_temp = step_len*3 + month_ori
year_new = year_ori + month_temp//12
month_new = math.ceil(month_temp % 12/3)*3
day_new = days_sum[month_new // 3 - 1]
return "%4d%02d%02d" % (year_new, month_new, day_new)
if __name__ == "__main__":
print(get_quarter_date("20190228", 0))
print(get_quarter_date("20190228", 1))
print(get_quarter_date("20190228", 5))
print(get_quarter_date("20190228", -2))
import time
import math
def slice_combination(array=[], need_print=False):
"""
算法思路:利用进制和位数进行巧妙遍历
数组的行数作为一个整数的位数
列数作为进制数
比如10行3列的数组,则组合结果有3的10次方种
此时遍历0到3^10,根据数值取其对应的位数和第几位的数值即可
比如数值为12,用三进制表示为110,则对应的数组为【第3行的第1个元素,第二行的第1个元素,第1行的第0个元素】
:param array: [], 二维数组
:param need_print: bool, 是否需要输出组合结果
:return:
"""
# 因为组合结果可能很大,此处结果不直接返回,改为直接print输出
if not array or not isinstance(array[0], list):
return
row_num = len(array) # 可以作为位数
col_num = len(array[0]) # 可以作为进制数
radix_digit = int(math.pow(col_num, row_num))
result_num = 0
for digit in range(radix_digit):
temp_combination = []
for i in range(row_num): # 遍历位数
temp = digit % col_num # 获取取第几个数
temp_combination.append(array[i][temp])
if need_print:
print(temp_combination) # 如果要输出结果,打开慈航注释即可
result_num += 1
return result_num
if __name__ == "__main__":
row_num = 10
col_num = 3
a = []
for i in range(row_num):
a.append([])
for j in range(col_num):
a[i].append(i + 0.1 * j % 1)
s1 = time.time()
result_num = slice_combination(a, need_print=False)
s2 = time.time()
print("{}行{}列的组合数总共有{}种,耗时{}s".format(row_num, col_num, result_num, s2 - s1))
row_num = 3
col_num = 3
a = []
for i in range(row_num):
a.append([])
for j in range(col_num):
a[i].append(i + 0.1 * j % 1)
s1 = time.time()
result_num = slice_combination(a, need_print=True)
s2 = time.time()
print("{}行{}列的组合数总共有{}种,耗时{}s".format(row_num, col_num, result_num, s2 - s1))
写一个函数,传递两个参数(旋转角度, 数组)求出旋转后数组的结果
例如
旋转 : 90°
数组:
1 2 3
4 5 6
7 8 9
结果为:
7 4 1
8 5 2
9 6 3
方法1:通过行列互换+行/列逆序来达到目的,两步操作皆可通过python简单实现
方法2:一个个元素取出来组装的新数组,注意1.第一个元素的位置 2.按行取还是按列取 3.正序还是逆序(这个方法理解起来简单一些)
import time
def reverse_row_col(ori_data):
"""
翻转数组的行列,行做列,列做行
:param ori_data:
:return: 输出一个对原始数组进行行列转换的新数组
"""
row_len = len(data)
col_len = len(data[0])
new_data = [[_ for _ in range(row_len)] for j in range(col_len)]
for i in range(row_len):
for j in range(col_len):
new_data[i][j] = ori_data[j][i]
return new_data
def reversed(a = []):
"""
对列表进行反转,如果参数是宫格(双维列表),则是行上下反转;
如果参数是宫格中的每行,则是列左右反转;
:param a: 列表
:return: 反转后的列表
"""
return [a[i] for i in range(len(a)-1, -1, -1)]
def rotate_array1(angle, data):
"""
1.如果为90度,为原数组行列调换+列反转
2.如果为180度,为原数组行反转+列反转
3.如果是270度,为原数组行列调换+行反转
4.如果是360度,为原数组
:param angle: 旋转角度,整数类型
:param data: 原数组
:return: 旋转后数组
"""
lst = list()
lst1 = reverse_row_col(data)
angle = angle % 360
if 90 == angle:
for row_dex, row in enumerate(lst1):
lst.append(reversed(row))
elif 180 == angle:
lst2 = reversed(data)
for row_dex, row in enumerate(lst2):
lst.append(reversed(row))
elif 270 == angle:
lst = reversed(data)
else:
lst = data
return lst
if __name__ == "__main__":
num = 3
data = [[j * num + i + 1 for i in range(0, num)] for j in range(0, num)]
print("旋转90度之后数组为:\n", rotate_array1(90, data))
print("旋转180度之后数组为:\n", rotate_array1(180, data))
print("旋转270度之后数组为:\n", rotate_array1(270, data))
print("旋转360度之后数组为:\n", rotate_array1(360, data))
'''
方法2:取元素法:按旋转的角度理解取元素的方法。注意三点:
1.从哪里取 2.按行取还是按列取 3.顺序取还是逆序取;
具体实现: 略
方法3:机械法:从方法1或者方法2的90度方法多次重复操作;
虽然代码量少,但是效率很低,不推荐
'''
某整数开方如何实现,保留指定位数
二分逼近,每一个位小到大去试,位从整数到小数一位位从左向右去确认;
如果该位上的数字与前面确定的数字拼接的数字,产生的平方值等于指定整数,该位最终数字即为该数字;
如果一旦大于指定指定整数,则该位最终数字为该数字减1;
import math
def sqr_digit(digit, bit_num):
"""
求保留指定位数小数的开方值,时间复杂度O(n),空间复杂度O(1)
:param digit: number
:param bit_num: int
:return: number
"""
if 0 > digit:
print("负数没有开方值")
return
if 0 > bit_num:
print("小数保留位数必须为非负数")
return
# 先确定整数部分
int_part = 0
for i in range(math.ceil(digit)+1):
if digit == i * i:
return i
if digit < i * i:
int_part = i - 1
break
result = int_part
# 再确定小数部分
for i in range(1, int(bit_num)+1):
temp = math.pow(10, -i)
for j in range(1, 11):
temp2 = result + temp * j
if digit == temp2 * temp2:
return temp2
if digit < temp2 * temp2:
result += temp * (j - 1)
break
return result
if __name__ == "__main__":
print(sqr_digit(-23, 2))
print(sqr_digit(123, 12))
print(sqr_digit(13, -2))
print(sqr_digit(16, 6))
print(sqr_digit(2, 0))