程序员的算法趣题 python3 - (2)

48 篇文章 0 订阅

注:以下题目来自《程序员的算法趣题》– [日]增井敏克著,原书解法主要用Ruby实现,最近在学Python,随便找点东西写写当做练习,准备改成Python3实现,顺便增加一些自己的理解。

6.考拉兹猜想

考拉兹猜想:
对自然数n:
若n为偶数,n /= 2
若n为奇数,n = n*3 + 1
循环操作,无论初始值为多少,最终得到1(1->4->2->1循环)
修改:
即使初始值为偶数,也 n = n*3 + 1,后面操作不变,问能回到初始值的偶数的个数。
求小于10000的偶数中,能回到初始值的数的个数。

思路:第一步变了,但后面没变,最终还是会得到1,所以如果到了1还没回到初始值,那么后面将会是(1, 4, 2, 1)循环,就再也回不到初始值了,当然需要特殊看看4,2
2,7,22,11,34,17,52,26,13,40,20,10,5,16,8,4,2
4,13,40,20,10,5,16,8,4
发现2,4先到初始值,后才到1,所以无需特殊处理

def solve(n):
    x = n*3 + 1
    while True:
        x = x // 2 if x % 2 == 0 else x*3 + 1
        if x == 1:
            return False
        if x == n:
            return True
    return False

cnt = 0
for i in range(0, 10001, 2):
    if solve(i):
        cnt += 1
print(cnt)

34

7.日期的二进制转换

把日期转换为YYYYMMDD这样的8位十进制数,然后用二进制表示,逆序排列二进制,再转成10进制,求与原来日期一致的日期。
求1964-10-10到2020-07-24之间满足上述条件的日期。
思路:等价于转成二进制之后,二进制表示是回文串的日期

from datetime import datetime, date, timedelta

def solve1(date_start, date_end):
    ans = []
    step = timedelta(days=1)
    d = datetime.strptime(date_start, '%Y-%m-%d').date()
    while d < datetime.strptime(date_end, '%Y-%m-%d').date():
        n = int(d.strftime('%Y%m%d'))
        s2 = format(n, 'b')
        if s2 == s2[::-1]:
            ans.append(n)
        d += step

    return ans

ans = solve1('1964-10-10', '2020-07-24')
print(ans)

使用python3 的datetime
[19660713, 19660905, 19770217, 19950617, 20020505, 20130201]

思路2:实现类似range的方法:

def date_range(start, stop, step):
    while start < stop:
        yield start
        start += step

def solve2(date_start, date_end):
    ans = []
    start = datetime.strptime(date_start, '%Y-%m-%d').date()
    end = datetime.strptime(date_end, '%Y-%m-%d').date()
    step = timedelta(days=1)
    for d in date_range(start, end, step):
        n = int(d.strftime('%Y%m%d'))
        s2 = format(n, 'b')
        if s2 == s2[::-1]:
            ans.append(n)
        d += step

    return ans

ans = solve2('1964-10-10', '2020-07-24')
print(ans)

[19660713, 19660905, 19770217, 19950617, 20020505, 20130201]

思路3:先找满足条件的数,再转成日期

def solve3(date_start, date_end):
    ans = []
    start = int(datetime.strptime(date_start, '%Y-%m-%d').date().strftime('%Y%m%d'))
    end = int(datetime.strptime(date_end, '%Y-%m-%d').date().strftime('%Y%m%d'))
    for i in range(start, end):
        s2 = format(i, 'b')
        if s2 == s2[::-1]:
            try:
                d = datetime.strptime(str(i), '%Y%m%d').date()
                ans.append(i)
            except:
                ...

    return ans

ans = solve3('1964-10-10', '2020-07-24')
print(ans)

[19660713, 19660905, 19770217, 19950617, 20020505, 20130201]

需要异常处理,因为有的数转不成合法日期,如2018-19-88

8.扫地机器人

假设有一款不会清扫同一位置的机器人,它只会前后左右移动,问移动12次时,路径数。
思路:不会清扫同一位置,意味着走过的位置不会再走。

def solve1(log, n):
    if len(log) == n+1:
        return 1

    directions = [(0, 1), (0, -1), (1, 0), (-1, 0)]
    count = 0
    for d in directions:
        next = log[-1][0] + d[0], log[-1][1] + d[1]
        if next not in log:
            count += solve1(log + [next], n)
    return count

start_time = time.clock()
ans = solve1([(0, 0)], n)
end_time = time.clock()
print('solve1: ', ans, 'time cost: ', end_time - start_time)

这是书中给出的方法,用一个list存已走过的位置,每次判断在list中的位置不再走。

优化:用二维数组表示走过的位置,加快判断是否走过的速度

def solve2(graph, pos, left):
    if left == 0:
        return 1

    directions = [(0, 1), (0, -1), (1, 0), (-1, 0)]
    count = 0
    for d in directions:
        next = pos[0] + d[0], pos[1] + d[1]
        if graph[next[0]][next[1]] == 0:
            graph[next[0]][next[1]] = 1
            count += solve2(graph, next, left-1)
            graph[next[0]][next[1]] = 0
    return count

start_time = time.clock()
graph = [[0] * n*2 for j in range(0, n*2)]
graph[0][0] = 1
ans = solve2(graph, (0, 0), n)
end_time = time.clock()
print('solve2: ', ans, 'time cost: ', end_time - start_time)

时间比较:
➜ n=13
solve1: 881500 time cost: 1.24
solve2: 881500 time cost: 0.76
➜ n=14
solve1: 2374444 time cost: 3.45
solve2: 2374444 time cost: 2.0300000000000002
➜ n=15
solve1: 6416596 time cost: 9.57
solve2: 6416596 time cost: 5.5

solve2快一些。

9.落单的男女

若干男女站成一排,求无论从哪个位置划分,都无法使两部分中男女人数均等的排列数。
求20男,10女满足条件的排列数。

如3男3女:
MMFMFF
如此排列无论从哪分开,两组的男女人数都不相等

思路:如果从左往右,出现了MF | xxxxx,MFFM | xxxxx, MMMFFF | xxxx这样的情况,即从左边开始出现了男女人数相等的情况,这个分组肯定不满足条件,同理右边存在这样的情况也不满足条件。

def solve():
    girls, boys = 10, 20

    dp = [ [0 for _ in range(boys+1)] for _ in range(girls+1) ]
    dp[0][0] = 1

    for g in range(girls+1):
        for b in range(boys+1):
            if g != b and boys-b != girls-g:
                dp[g][b] += dp[g-1][b] if g >= 1 else 0
                dp[g][b] += dp[g][b-1] if b >= 1 else 0

    return dp[girls-1][boys] + dp[girls][boys-1]

ans = solve()
print(ans)

2417416

def solve1(girls, boys):
    dp = [ [0 for _ in range(boys+1)] for _ in range(girls+1) ]
    dp[0][0] = 1

    for g in range(girls+1):
        for b in range(boys+1):
            if g != b and boys-b != girls-g:
                dp[g][b] += dp[g-1][b] if g >= 1 else 0
                dp[g][b] += dp[g][b-1] if b >= 1 else 0

    return dp[girls-1][boys] + dp[girls][boys-1]

ans = solve1(10, 20)
print(ans)

ans = solve1(20, 10)
print(ans)

ans = solve1(3, 3)
print(ans)

2417416
2417416
4

10.轮盘的最大值

这里写图片描述

欧式规则轮盘数字:
0, 32, 15, 19, 4, 21, 2, 25, 17, 34, 6, 27, 13, 36, 11, 30, 8, 23, 10, 5, 24, 16, 33, 1, 20, 14, 31, 9, 22, 18, 29, 7, 28, 12, 35, 3, 26
美式规则:
0, 28, 9, 26, 30, 11, 7, 20, 32, 17, 5, 22, 34, 15, 3, 24, 36, 13, 1, 00, 27, 10, 25, 29, 12, 8, 19, 31, 18, 6, 21, 33, 16, 4, 23, 35, 14, 2
问,当2<=n<=36时,求连续n个数的和最大的情况,满足“欧式规则下和小于美式规则的和”的n的个数

def max_sum(nums, n):
    s = ans = sum(nums[:n])
    length = len(nums)
    for i in range(length):
        s += nums[(i+n) % length] - nums[i]
        ans = max(ans, s)
    return ans

def solve():
    european = [
            0, 32, 15, 19, 4, 21, 2, 25, 17, 34, 6, 27, 13, 36, 
            11, 30, 8, 23, 10, 5, 24, 16, 33, 1, 20, 14, 31, 9, 
            22, 18, 29, 7, 28, 12, 35, 3, 26 ]
    american = [
            0, 28, 9, 26, 30, 11, 7, 20, 32, 17, 5, 22, 34, 15, 
            3, 24, 36, 13, 1, 0, 27, 10, 25, 29, 12, 8, 19, 31, 
            18, 6, 21, 33, 16, 4, 23, 35, 14, 2 ]

    ans = []
    for i in range(2, 37):
        if max_sum(european, i) < max_sum(american, i):
            ans += [i]
    return ans

ans = solve()
print(ans)
print(len(ans))

思路:模拟

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值