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

48 篇文章 0 订阅

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

16.三根绳子折成四边形

三根绳子折成3个四边形,一根摆成正方形,两根摆成长方形,会出现长方形面积之和等于正方形面积的情况,如:
绳子长度25,
1*9 = 9
2*8 = 16
5*5 = 25

如果将同比整数倍的结果看成同一种解法,如
绳长40,
2*18 = 36
4*16 = 64
10*10 = 100
跟上面看成一种解法。

求绳子长度从1到500时,共有多少种组合满足条件。

思路1:暴力搜索

def solve1(n):
    ans = set()
    for c in range(1, n // 4 + 1):
        s = c * c
        for a in range(1, c):
            sa = a * (2*c - a)
            for b in range(1, a):
                sb = b * (2*c - b)
                if s == sa + sb:
                    ans |= {1.0 * sb / sa}
    return ans

s = time.perf_counter()
ans = solve1(2000)
e = time.perf_counter()
print(ans)
print(len(ans))
print('time cost', e - s)

{0.5625, 0.1736111111111111, 0.28444444444444444, 0.9070294784580499, 0.3871604938271605, 0.34725765306122447, 0.13270408163265307, 0.6702917136896823, 0.011141975308641975, 0.3296627824454568, 0.015747789311803154, 0.4797511291017785, 0.36922501929012347, 0.19411048099138836, 0.47175207756232684, 0.02395124716553288, 0.7616528925619834, 0.040812162024283234, 0.7024036281179138, 0.6501585714508897, 0.043737287352071004, 0.126303585159554, 0.03817262198997246, 0.013937114197530864, 0.8539740114795918, 0.006420529257067719, 0.8397223140495867, 0.012422241179346106, 0.20825459813555053, 0.09932047750229568, 0.10488765495867769, 0.07370856452570586, 0.05692828562634657, 0.005502052892162782, 0.09419090665288689, 0.007590105601469238, 0.004767573696145125, 0.9544775796398892, 0.03361111111111111, 0.3624120535564479, 0.4620445127942244, 0.14142742347870552, 0.15105967971938566, 0.14708968233471073, 0.08506944444444445, 0.11755102040816326, 0.050625, 0.01793686224489796, 0.028167636559244952, 0.2185866081969978, 0.24817185256745697, 0.059295412379827966, 0.009111570247933885, 0.6842695717993079, 0.4921562130177515, 0.26278568513417, 0.1096911753963036, 0.020618014464168312, 0.4347301050597754, 0.03694840523854657, 0.15625743944636677, 0.23765625, 0.6363584711163058, 0.07722548476454294, 0.25483080621301774, 0.06449987402368355, 0.07042899408284024, 0.4500173611111111, 0.04532008881984166, 0.010050188126959002, 0.41465794306703396, 0.9834027777777777, 0.3090051903114187, 0.7851929012345679, 0.008298719613869493, 0.8150077160493827, 0.7268655489809336, 0.20177871829334554, 0.31976332199546487, 0.004171006944444444}
80
time cost 1.9866863046772778

思路2:可用面积组合

def solve2(n):
    ans = set()
    for c in range(1, n // 4 + 1):
        s = c * c
        cands = [ i * (2*c - i) for i in range(1, c) ]
        for sa, sb in combinations(cands, 2):
            if sa + sb == s:
                ans |= {1.0 * sa / sb}
    return ans

s = time.perf_counter()
ans = solve2(2000)
e = time.perf_counter()
print(ans)
print(len(ans))
print('time cost', e - s)

{0.5625, 0.1736111111111111, 0.28444444444444444, 0.9070294784580499, 0.3871604938271605, 0.34725765306122447, 0.13270408163265307, 0.6702917136896823, 0.011141975308641975, 0.3296627824454568, 0.015747789311803154, 0.4797511291017785, 0.36922501929012347, 0.19411048099138836, 0.47175207756232684, 0.02395124716553288, 0.7616528925619834, 0.040812162024283234, 0.7024036281179138, 0.6501585714508897, 0.043737287352071004, 0.126303585159554, 0.03817262198997246, 0.013937114197530864, 0.8539740114795918, 0.006420529257067719, 0.8397223140495867, 0.012422241179346106, 0.20825459813555053, 0.09932047750229568, 0.10488765495867769, 0.07370856452570586, 0.05692828562634657, 0.005502052892162782, 0.09419090665288689, 0.007590105601469238, 0.004767573696145125, 0.9544775796398892, 0.03361111111111111, 0.3624120535564479, 0.4620445127942244, 0.14142742347870552, 0.15105967971938566, 0.14708968233471073, 0.08506944444444445, 0.11755102040816326, 0.050625, 0.01793686224489796, 0.028167636559244952, 0.2185866081969978, 0.24817185256745697, 0.059295412379827966, 0.009111570247933885, 0.6842695717993079, 0.4921562130177515, 0.26278568513417, 0.1096911753963036, 0.020618014464168312, 0.4347301050597754, 0.03694840523854657, 0.15625743944636677, 0.23765625, 0.6363584711163058, 0.07722548476454294, 0.25483080621301774, 0.06449987402368355, 0.07042899408284024, 0.4500173611111111, 0.04532008881984166, 0.010050188126959002, 0.41465794306703396, 0.9834027777777777, 0.3090051903114187, 0.7851929012345679, 0.008298719613869493, 0.8150077160493827, 0.7268655489809336, 0.20177871829334554, 0.31976332199546487, 0.004171006944444444}
80
time cost 0.9592412677593529

思路3:可用边长组合

def solve3(n):
    ans = set()
    for c in range(1, n // 4 + 1):
        s = c * c
        cands = [ i for i in range(1, c) ]
        for a, b in combinations(cands, 2):
            sa, sb = a*a, b*b
            if sa + sb == s:
                ans |= {1.0 * sa / sb}
    return ans

s = time.perf_counter()
ans = solve3(2000)
e = time.perf_counter()
print(ans)
print(len(ans))
print('time cost', e - s)

{0.5625, 0.1736111111111111, 0.28444444444444444, 0.9070294784580499, 0.3871604938271605, 0.34725765306122447, 0.13270408163265307, 0.6702917136896823, 0.011141975308641975, 0.3296627824454568, 0.015747789311803154, 0.4797511291017785, 0.36922501929012347, 0.19411048099138836, 0.47175207756232684, 0.02395124716553288, 0.7616528925619834, 0.040812162024283234, 0.7024036281179138, 0.6501585714508897, 0.043737287352071004, 0.126303585159554, 0.03817262198997246, 0.013937114197530864, 0.8539740114795918, 0.006420529257067719, 0.8397223140495867, 0.012422241179346106, 0.20825459813555053, 0.09932047750229568, 0.10488765495867769, 0.07370856452570586, 0.05692828562634657, 0.005502052892162782, 0.09419090665288689, 0.007590105601469238, 0.004767573696145125, 0.9544775796398892, 0.03361111111111111, 0.3624120535564479, 0.4620445127942244, 0.14142742347870552, 0.15105967971938566, 0.14708968233471073, 0.08506944444444445, 0.11755102040816326, 0.050625, 0.01793686224489796, 0.028167636559244952, 0.2185866081969978, 0.24817185256745697, 0.059295412379827966, 0.009111570247933885, 0.6842695717993079, 0.4921562130177515, 0.26278568513417, 0.1096911753963036, 0.020618014464168312, 0.4347301050597754, 0.03694840523854657, 0.15625743944636677, 0.23765625, 0.6363584711163058, 0.07722548476454294, 0.25483080621301774, 0.06449987402368355, 0.07042899408284024, 0.4500173611111111, 0.04532008881984166, 0.010050188126959002, 0.41465794306703396, 0.9834027777777777, 0.3090051903114187, 0.7851929012345679, 0.008298719613869493, 0.8150077160493827, 0.7268655489809336, 0.20177871829334554, 0.31976332199546487, 0.004171006944444444}
80
time cost 1.8917626161128283

思路4:使用GCD 判断是否是同比组合

def solve4(n):
    ans = []
    for c in range(1, n // 4 + 1):
        s = c * c
        cands = [ i for i in range(1, c) ]
        for a, b in combinations(cands, 2):
            if a*a + b*b == s:
                if math.gcd(a, b) == 1:
                    ans.append((a, b, c))
    return ans

s = time.perf_counter()
ans = solve4(2000)
e = time.perf_counter()
print(ans)
print(len(ans))
print('time cost', e - s)

[(3, 4, 5), (5, 12, 13), (8, 15, 17), (7, 24, 25), (20, 21, 29), (12, 35, 37), (9, 40, 41), (28, 45, 53), (11, 60, 61), (16, 63, 65), (33, 56, 65), (48, 55, 73), (13, 84, 85), (36, 77, 85), (39, 80, 89), (65, 72, 97), (20, 99, 101), (60, 91, 109), (15, 112, 113), (44, 117, 125), (88, 105, 137), (17, 144, 145), (24, 143, 145), (51, 140, 149), (85, 132, 157), (119, 120, 169), (52, 165, 173), (19, 180, 181), (57, 176, 185), (104, 153, 185), (95, 168, 193), (28, 195, 197), (84, 187, 205), (133, 156, 205), (21, 220, 221), (140, 171, 221), (60, 221, 229), (105, 208, 233), (120, 209, 241), (32, 255, 257), (23, 264, 265), (96, 247, 265), (69, 260, 269), (115, 252, 277), (160, 231, 281), (161, 240, 289), (68, 285, 293), (136, 273, 305), (207, 224, 305), (25, 312, 313), (75, 308, 317), (36, 323, 325), (204, 253, 325), (175, 288, 337), (180, 299, 349), (225, 272, 353), (27, 364, 365), (76, 357, 365), (252, 275, 373), (135, 352, 377), (152, 345, 377), (189, 340, 389), (228, 325, 397), (40, 399, 401), (120, 391, 409), (29, 420, 421), (87, 416, 425), (297, 304, 425), (145, 408, 433), (84, 437, 445), (203, 396, 445), (280, 351, 449), (168, 425, 457), (261, 380, 461), (31, 480, 481), (319, 360, 481), (44, 483, 485), (93, 476, 485), (132, 475, 493), (155, 468, 493)]
80
time cost 1.7553507732227445

思路5:设正方形边长c,则长方形长和宽和为 c*2
设分别为c+x, c-x, 则s1 = c*c - x*x,则另一个长方形面积s2 = c*c - c*c + x*x = x*x,
即s2是平方数,同理s1也是平方数。
因此可以事先算好范围内所有平方数,然后查找判断

def solve5(n):
    ans = []
    squars = { i*i : i for i in range(n//4 + 1) }
    for c in range(1, n // 4 + 1):
        s = c * c
        for a in range(1, c):
            sa = a*a
            sb = s - sa
            if sb in squars and sb < sa:
                b = squars[s - a*a]
                if math.gcd(a, b) == 1:
                    ans.append((a, b, c))
    return ans

s = time.perf_counter()
ans = solve5(2000)
e = time.perf_counter()
print(ans)
print(len(ans))
print('time cost', e - s)

[(4, 3, 5), (12, 5, 13), (15, 8, 17), (24, 7, 25), (21, 20, 29), (35, 12, 37), (40, 9, 41), (45, 28, 53), (60, 11, 61), (56, 33, 65), (63, 16, 65), (55, 48, 73), (77, 36, 85), (84, 13, 85), (80, 39, 89), (72, 65, 97), (99, 20, 101), (91, 60, 109), (112, 15, 113), (117, 44, 125), (105, 88, 137), (143, 24, 145), (144, 17, 145), (140, 51, 149), (132, 85, 157), (120, 119, 169), (165, 52, 173), (180, 19, 181), (153, 104, 185), (176, 57, 185), (168, 95, 193), (195, 28, 197), (156, 133, 205), (187, 84, 205), (171, 140, 221), (220, 21, 221), (221, 60, 229), (208, 105, 233), (209, 120, 241), (255, 32, 257), (247, 96, 265), (264, 23, 265), (260, 69, 269), (252, 115, 277), (231, 160, 281), (240, 161, 289), (285, 68, 293), (224, 207, 305), (273, 136, 305), (312, 25, 313), (308, 75, 317), (253, 204, 325), (323, 36, 325), (288, 175, 337), (299, 180, 349), (272, 225, 353), (357, 76, 365), (364, 27, 365), (275, 252, 373), (345, 152, 377), (352, 135, 377), (340, 189, 389), (325, 228, 397), (399, 40, 401), (391, 120, 409), (420, 29, 421), (304, 297, 425), (416, 87, 425), (408, 145, 433), (396, 203, 445), (437, 84, 445), (351, 280, 449), (425, 168, 457), (380, 261, 461), (360, 319, 481), (480, 31, 481), (476, 93, 485), (483, 44, 485), (468, 155, 493), (475, 132, 493)]
80
time cost 0.010256188921630383

17.挑战30人31足

该项目中若多个女生连续排列,体力上会出于劣势。求30人排成一排,有多少有利的排列?(只考虑男女排列,不考虑具体人)。如四个人时有8种组合:
BBBB
BBBG
BBGB
BGBB
GBBB
BGBG
GBBG
GBGB

思路:如果已有n个人,如果最右边是男生,则可以加一个男生或一个女生;如果最右边是女生,则只能加一个男生。

1)用一个串表示队伍,则当队伍还没人,或者队伍最右边是男生时可以加一个女生,而无论男女都可以加男生:

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

    ans = solve1(n, current + 'B')
    if len(current) == 0 or current[-1] == 'B':
        ans += solve1(n, current + 'G')

    return ans

s = time.perf_counter()
ans = solve1(30, '')
e = time.perf_counter()
print(ans)
print('time cost: ', e-s)

2178309
time cost: 1.422395246103406

2)类似

def solve2(n, current):
    if len(current) == n:
        return 1

    ans = solve1(n, current + 'B')
    if current[-1] == 'B':
        ans += solve1(n, current + 'G')

    return ans

s = time.perf_counter()
ans = solve2(30, 'B') + solve2(30, 'G')
e = time.perf_counter()
print(ans)
print('time cost: ', e-s)

2178309
time cost: 1.42152512492612

3)只记录最后一人是男还是女

def solve3(n, last):
    if n == 0:
        return 1

    if last == 'B':
        return solve3(n-1, 'B') + solve3(n-1, 'G')
    else:
        return solve3(n-1, 'B')


s = time.perf_counter()
ans = solve3(29, 'B') + solve3(29, 'G')
e = time.perf_counter()
print(ans)
print('time cost: ', e-s)

2178309
time cost: 0.6548308739438653

4)

def solve4(n, d):
    if n == 1 or n == 2:
        return n+1

    if n in d:
        return d[n]

    d[n] = solve4(n-1, d) + solve4(n-2, d)
    return d[n]

s = time.perf_counter()
d = {}
ans = solve4(30, d)
e = time.perf_counter()
print(ans)
print('time cost: ', e-s)

2178309
time cost: 1.64741650223732e-05

思路:斐伯那契数列
从4)及打印前几个数的规律,发现可以用斐伯那契数列求解

for i in range(10):
    print(solve1(i, ''), end=', ')
print()

1, 2, 3, 5, 8, 13, 21, 34, 55, 89,

def solve5(n):
    b, g = 1, 0
    for i in range(n):
        b, g = b + g, b
    return b + g

s = time.perf_counter()
ans = solve5(30)
e = time.perf_counter()
print(ans)
print('time cost: ', e-s)

2178309
time cost: 3.1921081244945526e-06

18.水果酥饼日

一个圆形蛋糕边缘均匀点缀着草莓,要求切成N块,N块蛋糕要求各有1~N个草莓(共N*(N+1)/2个草莓)。
求满足相邻两块蛋糕草莓数和为平方数的最小的N。

思路:DFS

def solve1(total, prev, used, squares):
    if total == len(used):
        if prev+1 in squares:
            return True
    else:
        for i in range(2, total+1):
            if (i+prev) in squares and i not in used:
                if solve1(total, i, used+[i], squares):
                    return True
    return False

def test1():
    start = time.perf_counter()
    n = 2
    while True:
        squares = { i*i for i in range(2, int(n ** 2)) }
        if solve1(n, 1, [1], squares):
            print(n)
            break
        n += 1
    end = time.perf_counter()
    print('solve1 time cost: ', end - start)

32
solve1 time cost: 0.3892214992083609

优化判断是否已使用的方式:

def solve2(total, left, prev, used, squares):
    if left == 0:
        if prev+1 in squares:
            return True
    else:
        for i in range(2, total+1):
            if not used[i] and (i+prev) in squares:
                used[i] = True
                if solve2(total, left-1, i, used, squares):
                    return True
                used[i] = False
    return False


def test2():
    start = time.perf_counter()
    n = 2
    while True:
        squares = { i*i for i in range(2, int(n ** 2)) }
        used = [False] * (n+1)
        used[1] = True
        if solve2(n, n-1, 1, used, squares):
            print(n)
            break
        n += 1
    end = time.perf_counter()
    print('solve2 time cost: ', end - start)

32
solve2 time cost: 0.29970831889659166

求出切蛋糕的顺序:

def solve3(total, prev, used, squares):
    if total == len(used):
        if prev+1 in squares:
            print(used)
            return True
    else:
        for i in range(2, total+1):
            if (i+prev) in squares and i not in used:
                if solve3(total, i, used+[i], squares):
                    return True
    return False

def test3():
    start = time.perf_counter()
    n = 2
    while True:
        squares = { i*i for i in range(2, int(n ** 2)) }
        ans = solve3(n, 1, [1], squares)
        if ans:
            print(ans)
            break
        n += 1
    end = time.perf_counter()
    print('solve3 time cost: ', end - start)

[1, 8, 28, 21, 4, 32, 17, 19, 30, 6, 3, 13, 12, 24, 25, 11, 5, 31, 18, 7, 29, 20, 16, 9, 27, 22, 14, 2, 23, 26, 10, 15]
True
solve3 time cost: 0.40476082591339946

seq = []
def solve4(total, left, prev, used, squares):
    if left == 0:
        if prev+1 in squares:
            return True
    else:
        for i in range(2, total+1):
            if not used[i] and (i+prev) in squares:
                used[i] = True
                seq.append(i)
                if solve4(total, left-1, i, used, squares):
                    return True
                used[i] = False
                seq.pop()
    return False


def test4():
    start = time.perf_counter()
    n = 2
    while True:
        squares = { i*i for i in range(2, int(n ** 2)) }
        used = [False] * (n+1)
        used[1] = True
        global seq
        seq = [1]
        if solve4(n, n-1, 1, used, squares):
            print(n)
            break
        n += 1
    end = time.perf_counter()
    print(seq)
    print('solve2 time cost: ', end - start)

[1, 8, 28, 21, 4, 32, 17, 19, 30, 6, 3, 13, 12, 24, 25, 11, 5, 31, 18, 7, 29, 20, 16, 9, 27, 22, 14, 2, 23, 26, 10, 15]
solve2 time cost: 0.3256980921141803

19.朋友的朋友也是朋友吗

假设有相同约数(不包括1)的数字互为好友,从1~N任选一个合数,从它开始,要经历几层好友才能和其他所有数产生联系。
求从1~N选7个合数时,最多经过6层就可以与其他所有数产生联系的最小的N。

思路:从1~N选7个合数,最多经过6层,可知我们要找的是由2个数相乘得到的数字组合。
设a~h互质,当7个合数为
a*b, b*c, c*d, d*e, e*f, f*g, g*h时,a经过6次可与g产生联系,5次可与f产生联系,4次可与e产生联系….
b经过1次可与a,c产生联系,5次可与g产生联系…
而所要求的答案是a*b, b*c … g*h中的最大值。
而当7个数为
a*a, a*b, b*c, c*d, d*e, e*f, f*f时得到的结果更小。

from itertools import permutations

def is_prime(n):
    if n <= 1:
        return False

    for i in range(2, n):
        if n % i == 0:
            return False

    return True

def solve(n):
    primes = []
    x = 2
    while len(primes) < n:
        if is_prime(x):
            primes += [x]
        x += 1

    ans = (primes[-1] * primes[-1], [])
    for perm in permutations(primes):
        friends = [perm[0] * perm[0], perm[-1] * perm[-1]] + [perm[i]*perm[i-1] for i in range(1, len(perm))]
        if max(friends) < ans[0]:
            ans = max(friends), friends
    return ans

ans = solve(6)
print(ans)

(55, [4, 49, 26, 39, 33, 55, 35])

20.受难立面魔方阵

1 14 14 4
11 7 6 9
8 10 10 5
13 2 3 15
上面叫“受难立面”,横纵对角线相加,和都为33,
如果从这些数字中任选任意个数,和为33的有310种,若限定4个数,则有88种。
求”和相同的组合“ 种数最多的和。

思路:组合

import itertools
import time

square = [1, 14, 14, 4, 11, 7, 6, 9,
          8, 10, 10, 5, 13, 2, 3, 15]

def solve1(square):
    d = {}
    for size in range(1, len(square)):
        combs = itertools.combinations(square, size)
        for t in combs:
            s = sum(t)
            if s in d:
                d[s] += 1
            else:
                d[s] = 1

    m = max(d, key=d.get)
    print(m, d[m])

s = time.perf_counter()
solve1(square)
e = time.perf_counter()
print("time cost: ", e-s)

66 1364
time cost: 0.018467612098902464

思路:DP

def solve2(square):
    all = sum(square)
    dp = [0] * (all + 1)
    dp[0] = 1

    for n in square:
        for i in range(all - n, -1, -1):
            dp[i + n] += dp[i]

    m = max(dp)
    print(dp.index(m), m)

s = time.perf_counter()
solve2(square)
e = time.perf_counter()
print("time cost: ", e-s)

66 1364
time cost: 0.00016872398555278778

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值