• 一道算法题:拼数字


    题目描述

    给定数字5、-5,7,-7,12,-12,每个数字个数无限多,从中选择尽量少的数字,使它们的和为N。

    分析

    因为对称性,只需要考虑N>0的情况。
    因为存在一对互质的正整数(比如5和7),所以总是存在整数x,y使5x+7y=1,只要能够产生1,就一定能够产生任意正整数。因此问题一定有解。

    方法一:打表法

    自下而上的更新
    缺点是比较暴力,占用空间比较大

    a = [5, 7, 12, -5, -7, -12]
    b = dict()
    for i in a:
        b[i] = 1
    while 2000 not in b:
        c = dict()
        for i in b:
            for j in a:
                k = i + j
                if k not in b:
                    c[k] = b[i] + 1
                elif b[i] + 1 < b[k]:
                    b[k] = b[i] + 1
        if len(c) == 0:
            break
        else:
            for i in c:
                b[i] = c[i]
    ans = sorted(list(b.items()))
    ans = [i for i in ans if i[0] >= 0]
    with open("test.txt", "w") as f:
        for i in ans:
            f.write("%d %d
    " % (i[0], i[1]))
    
    

    方法二:循环法

    最终答案只有两种选择:
    5,-7,12
    -5,7,12
    其余选择必然不是最优解:选了+5就不能选择-5,选了5和7,就不能选-12(抵消了),5、7同时选必然不如选择一个12。
    并且5的个数不会超过7,7的个数不会超过12,否则的话,必然不是最优解。

    对于给定的正负数数组a,首先对a进行从小到大排序,a[0]的个数不能超过a[1],a[1]的个数不能超过a[2],a[2]的个数不能超过a[3]......

    a = [5, 7, 12, -5, -7, -12]
    
    
    def f(m, x):
        ans = 0xfffffff
        for i in range(12):
            for j in range(12):
                left = x - m[0] * i - m[1] * j
                if left % m[2] == 0:
                    ans = min(ans, i + j + abs(left // m[2]))
        return ans
    
    
    def solve(x):
        return min(f([5, -7, 12], x), f([-5, 7, 12], x))
    
    
    def output():
        with open("f1.txt", 'w') as file:
            for i in range(500):
                file.write("%d %d
    " % (i, solve(i)))
    
    
    output()
    

    方法三:方法一和方法二的综合

    对于5,7,12三个数字,5的个数肯定不会超过7,7的个数肯定不会超过12。
    又因为只有5,-7,12和-7,5,12两种组合,所以有77+30=107
    只需要打表出0~107,然后其余部分用12填充即可。

    a = [5, 7, 12, -5, -7, -12]
    b = dict()
    for i in a:
        b[i] = 1
    limit=43
    while limit*3 not in b:
        c = dict()
        for i in b:
            for j in a:
                k = i + j
                if k not in b:
                    c[k] = b[i] + 1
                elif b[i] + 1 < b[k]:
                    b[k] = b[i] + 1
        if len(c) == 0:
            break
        else:
            for i in c:
                b[i] = c[i]
    
    
    def solve(x):
        if x <= limit:
            return b[x]
        cnt = x // 12
        ans = 0xffffff
        while x - cnt * 12 < limit:
            ans = min(ans, cnt + b[x - cnt * 12])
            cnt -= 1
        return ans
    
    

    经过测试,这个问题中的limit至少为43,这是为什么呢?

    更一般的提法

    给定正整数数组a[N],从而确定了+a[0],-a[0],+a[1],-a[1]......共2N个整数,从中选择尽量少的数字,使得它们的和为M。
    只需打表处理0~a[0]*a[1]+a[1]*a[2]+a[2]*a[3]+a[3]*a[4]范围内的结果,其余部分必定以最大值a[N-1]填充。

  • 相关阅读:
    [转]实现财务自由的6大步骤
    [转] js 实现table每列可左右拖动改变列宽度
    [转]在Excel中使用SQL语句实现精确查询
    [书目20150727]有效沟通-余世维
    [书目20150727]选择重于一切(扎克伯格给年轻人的37个人生忠告)
    [转]把汉字写“绝”了
    [转]《间架结构92法》
    深圳社保转入
    广州社保转出
    [转]利用telnet进行SMTP的验证
  • 原文地址:https://www.cnblogs.com/weiyinfu/p/7779825.html
Copyright © 2020-2023  润新知