zoukankan      html  css  js  c++  java
  • 求解分组问题(百度面试题)

    题目描述:任意数分三组,使得每组的和尽量相等

    思路分析:看完题目稍作分析的后,想不到用什么算法解决这个问题,于是思路如其他人一样。

    先排序,初始化三个空数组,每次从给出的数组中拿一个最大值放到项数和最小的数组中。

    python实现

    from operator import add
    from functools import reduce
    
    def sep(n,l):
        #降序排序
        l = sorted(l,reverse=True)
        group = l[:n]
        for i in l[n:]:
            #往总和最小的分组里添加
            group[n-1] += i
            group = sorted(group,reverse=True)
        return group

    这里比较简单的直接求分组的项数和,并没有求出分组的情况。

    而上网查了一下发觉有第二种思路,先把任意数求和再除以3求得分组的平均数,然后排序并遍历所有数,逐个放入初始化的3个空数组中,

    当第一个数组尽量靠近平均值,然后转而放进第二个数组,当第二个数组也尽量靠近平均数后,剩余所有数放进第三个数组。

    Python实现

    def sep2(n,l):
        group = []
        l = sorted(l,reverse=True)
        #求平均值
        ave = reduce(add,l) // n
        sum = 0
        #往分组里添加直至 总和大于等于平均值
        for i in l:
            #sum += i
            sum = ifAdd(ave,sum,i)
            if sum >= ave and len(group) < 3:
                group.append(sum)
                sum = 0
        if sum !=0:
            group.append(sum)
        return group
        
    def ifAdd(ave,sum,n):
        if (sum+n > ave) and (sum+n-ave > ave-sum):
            return sum
        else:
            return sum+n

    但是,当测试数据时候,发现这2种方法都并各自的缺陷。(当然我写得相对比较粗糙,或许改进一下会比较靠近,但是问题依然存在)

    list1 = [3, 8, 20, 15, 60, 1, 32]
    list2 = [16,9,7,6,5,5]
    n = 3

    测试结果如下

    sep1
    [60, 40, 39]
    [60, 52, 27]

    sep2
    [18, 16, 14]
    [16, 16, 16]

    于是请教了董老师(董付国),他给出的思路如下。先分成几个子列表,然后动态调整各子列表的元素,从元素之和最大的子列表中拿出最小的元素放到元素之和最小的子列表中,

    尽管不能保证每次都能成功,但是已经相当接近了。

    import random
    
    def numberSplit(lst, n,threshold):
        '''lst为原始列表,内含若干整数,n为拟分份数
           threshold为各子列表元素之和的最大差值'''
        length = len(lst)
        p = length // n
        #尽量把原来的lst列表中的数字等分成n份
        partitions = []
        for i in range(n-1):
            partitions.append(lst[i*p:i*p+p])
        else:
            partitions.append(lst[i*p+p:])
    
        print('初始分组结果:', partitions)
        
        #不停地调整各个子列表中的数字
        #直到n个子列表中数字之和尽量相等
        times = 0
        while times < 1000:
            times += 1
            maxLst = max(partitions, key=sum)
            minLst = min(partitions, key=sum)
            #把大的子列表中最小的元素调整到小的子列表中
            m = min(maxLst)
            i = [index for index, value in enumerate(maxLst) if value==m][0]
            minLst.insert(0, maxLst.pop(i))
            print('第{0}步处理结果:'.format(times), partitions)
            first = sum(partitions[0])
            for item in partitions[1:]:
                if abs(sum(item)-first) > threshold:
                    break
            else:
                break
        else:
            print('很抱歉,我无能为力,只能给出这样一个结果了。')
        return partitions
    
    lst = [random.randint(1, 100) for i in range(10)]
    print(lst)
    result = numberSplit(lst, 3, 10)
    print('最终结果:', result)
    #输出各组数字之和
    print('各子列表元素之和:')
    for item in result:
        print(sum(item))

    如果有更加完美或者更加适合的解题思路,欢迎在留言区指点。

  • 相关阅读:
    火狐获取图片宽和高的方法
    JDBC连接本地sqlserver2005的方法
    war文件不在tomcat 的webapps运行
    javascript 去除空格 方法
    火狐显示图片的方法
    八款开源Android游戏引擎
    android 模拟器 hardWare 属性说明
    jqgrid 属性说明
    Java命名规范
    在MyEclipse里怎样一次性取消所有断点
  • 原文地址:https://www.cnblogs.com/lateink/p/6440636.html
Copyright © 2011-2022 走看看