注:以下题目来自《程序员的算法趣题》– [日]增井敏克著,原书解法主要用Ruby实现,最近在学Python,随便找点东西写写当做练习,准备改成Python3实现,顺便增加一些自己的理解。
1.回文数
求十进制、二进制、八进制表示都是回文数的数字中,大于十进制数10的最小值。
思路:转成字符串,比较是否跟自己反转后相同
num = 11
while True:
s10 = str(num)
s2 = str(bin(num))[2:]
s8 = str(oct(num))[2:]
if s10 == s10[::-1] and s2 == s2[::-1] and s8 == s8[::-1]:
break
num += 2
print(num, bin(num), oct(num))
585 0b1001001001 0o1111
python转成2进制会在前面加0b,八进制会在前面加0o,所以s2, s8都取了[2 :]切片。
使用format:
num = 11
while True:
s10 = str(num)
s2 = format(num, 'b')
s8 = format(num, 'o')
if s10 == s10[::-1] and s2 == s2[::-1] and s8 == s8[::-1]:
break
num += 2
print(num, bin(num), oct(num))
585 0b1001001001 0o1111
2.四则运算
一个数,可以在数字间插入运算符+, -, *, / 也可以不插入,求至少插入一个运算符且满足结果为原来数字逆序的数,如:
351 -> 3*51 = 153
621 -> 6*21 = 126
对于三位数:
ops = ['+', '-', '*', '/', '']
def solve3():
for i in range(100, 1000):
s = str(i)
for op1 in ops:
for op2 in ops:
exp = s[0] + op1 + s[1] + op2 + s[2]
if len(exp) > len(s):
try:
if s[::-1] == str(eval(exp)):
ans.append(exp + '=' + s[::-1])
except:
pass
ans = []
solve3()
print(ans)
[‘3*51=153’, ‘6*21=126’, ‘8*86=688’]
四位数:
ops = ['+', '-', '*', '/', '']
def solve4():
for i in range(1000, 10000):
s = str(i)
for op1 in ops:
for op2 in ops:
for op3 in ops:
exp = s[0] + op1 + s[1] + op2 + s[2] + op3 + s[3]
if len(exp) > len(s):
try:
if s[::-1] == str(eval(exp)):
ans.append(exp + '=' + s[::-1])
except:
pass
ans = []
solve4()
print(ans)
[‘5*9*31=1395’]
如何避免靠嵌套for循环呢?
ops = ['+', '-', '*', '/', '']
def dfs(num, pos, exp):
s = str(num)
if pos == len(s):
if len(exp) > len(s):
try:
if s[::-1] == str(eval(exp)):
ans.append(exp + '=' + s[::-1])
except:
pass
return
for op in ops:
dfs(s, pos+1, exp+op+s[pos])
ans = []
for i in range(10, 6000):
if dfs(i, 1, str(i)[0]):
ans.append(i)
print(ans)
[‘3*51=153’, ‘6*21=126’, ‘8*86=688’, ‘5*9*31=1395’]
另一种写法:
ops = ['+', '-', '*', '/', '']
def dfs2(num, pos, exp):
s = str(num)
if pos == len(s)-1:
if len(exp) >= len(s):
exp = exp + s[-1]
try:
if s[::-1] == str(eval(exp)):
ans.append(exp + '=' + s[::-1])
except:
pass
return
for op in ops:
dfs2(s, pos+1, exp+s[pos]+op)
ans = []
for i in range(10, 6000):
if dfs2(i, 0, ''):
ans.append(i)
print(ans)
[‘3*51=153’, ‘6*21=126’, ‘8*86=688’, ‘5*9*31=1395’]
3.翻牌
100张牌,写着1~100,顺序排列,开始背面朝上,按如下顺序翻牌:
1)从第2张开始,隔1张牌翻牌;
2)从第3张开始,隔2张牌翻牌;
3)从第4张开始,隔3张牌翻牌;
….
直到没有可翻动的牌为止,求最后背面朝上的牌的数字
思路:模拟,用一个数组记录当前朝上还是朝下,模拟整个翻牌过程
def solve(n):
cards = [False] * n
for i in range(2, n+1):
for j in range(i-1, n, i):
cards[j] = not cards[j]
ans = []
for i in range(n):
if not cards[i]:
ans.append(i+1)
return ans
ans = solve(100)
print(ans)
[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
思路2:翻牌规律:
2, 4, 6, 8, 10, 12….
3, 6, 9, 12…
4, 8, 12…
5, 10, 15…
6, 12…
…
12…
12在2, 3, 4, 6, 12各被翻动一次,翻了奇数次,所以最后正面朝上
即每个数字都是在它的约数被翻转,模拟这个过程:
def solve2(n):
if n < 1:
return []
ans = [1]
for i in range(2, n+1):
flag = False
for j in range(2, i):
if i % j == 0:
flag = not flag
if flag:
ans.append(i)
return ans
ans = solve2(100)
print(ans)
[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
思路3:数字的规律,可以发现结果都是平方数,直接算平方数:
def solve3(n):
ans = []
i = 1
while i*i <= n:
ans.append(i*i)
i += 1
return ans
[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
4.切木棒
把n cm长的木棒切为1cm的小段,一根木棒只能一个人切,当切为两段时可由2人同时切,切为3段时可由3人同时切。
求长为n的木棒,m个人同时切,最少切几次?
如n=8, m=3,
8 -> 1人切,… 1次
4, 4 -> 2 人切,… 2次
2, 2, 2, 2 -> 3 人切, … 3次
1, 1, 1, 1, 1, 1, 2 -> 1 人切, … 4次
1, 1, 1, 1, 1, 1, 1, 1
求n=20, m=3时最少切几次? n = 100, m= 5 时最少切几次?
思路1:递归
def solve1(n, m, current):
if current >= n:
return 0
if current <= m:
return 1 + solve1(n, m, current*2)
else:
return 1 + solve1(n, m, current + m)
print(solve1(20, 3, 1))
print(solve1(100, 5, 1))
8
22
思路2:递推
def solve2(n, m):
ans, current = 0, 1
while current < n:
current += current if current < m else m
ans += 1
return ans
print(solve2(20, 3))
print(solve2(100, 5))
8
22
5.兑换硬币
一个机器可以用纸币兑换硬币,硬币有假设10,50,100,500四种,且机器最多兑换出15个硬币,比如1000纸币不能兑换出100个面值10的硬币。求兑换1000面值的纸币会有多少组合?(注日元)
思路1:多重循环
def solve1(n):
ans = 0
for c500 in range(0, n//500 + 1):
for c100 in range(0, n//100 + 1):
for c50 in range(0, n//50 + 1):
for c10 in range(0, n//10 + 1):
if c500 + c100 + c50 + c10 <= 15:
if 500*c500 + 100*c100 + 50*c50 + 10*c10 == n:
ans += 1
return ans
print(solve1(1000))
20
思路2:使用python内置的组合
import itertools
def solve2(n):
coins = [500, 100, 50, 10]
ans = 0
for i in range(2, 16):
for comb in itertools.combinations_with_replacement(coins, i):
if sum(comb) == n:
ans += 1
return ans
print(solve2(1000))
20
思路3:递归
def solve3(target, coins, max_num):
def dfs(target, index, left):
if index == len(coins):
if target == 0:
ans[0] += 1
else:
for i in range(0, target // coins[index] + 1):
if i <= left:
dfs(target - coins[index]*i, index+1, left-i)
ans = [0]
dfs(target, 0, max_num)
return ans[0]
print(solve3(1000, [500, 100, 50, 10], 15))
20