zoukankan      html  css  js  c++  java
  • 求数组内所有和为指定数的算法

      求数组内所有和为一个数的情况,第一眼觉得这题应该挺简单的吧,然后慢慢的做,后来发现这题有点东西,现在就把做出来的写一下。个人想法,而且代码可能不是很简洁,大佬们看到请指点!第一次写这种博客,格式搞好久,哭了!
      这题是这样的,假设数组里面有[1, 2, 3, 4, 5, 6],然后求所有和为6的情况,情况为[6],[5,1],[4,2],[3,2,1],这个数比较少,看着没啥难度,但是数一旦多起来,情况就会比较复杂了。为了解释方便,我就用和为20,数组为[1,2,...,20]来举例子讲述代码吧,话不多说,先看主要代码吧。

    def FNN(n): #首次遍历嵌套
        for i in range(len(num)-1, -1, -1): #后序遍历
            number = []     #这个数组是用来存储一次和为n的,比如[n],[n-1,1]
            plus = 0    #这个就是和了,判断和20的关系
            for j in range(i, -1, -1):  #二层循环,
                if plus + num[j] < n:    #累计求和,判断和n的关系
                    number.append(num[j])   #如果小于n,就加入数组num
                    plus = plus + num[j]    #这个时候才把和加到plus里面
                elif plus + num[j] == n:    #等于n的情况
                    number.append(num[j])
                    number = queue(number)    #将得到的数组排序成从大到小,作用后面会讲到
                    if number not in ReNumber:  #避免重复
                        ReNumber.append(number)     #将得到的一种情况存储起来
                        #print("主要:%s" % number)    #可以不用,但是可以让人很清楚的看结果
                    if len(number) > 1:     #防止[20]这个情况也会进入循环
                        for k in range(0, len(number)):     #开始调用另一个函数
                            t = number[k]      #把number[k]先转给t,不然下面的remove就把这个数给删了
                            number.remove(t)      #把number[k]先去了
                            GNN(number, t)    #此时传进去的就是没有number[k]的数组,因为我们要求t的和的情况
                            number.append(t)      #把删除的加回去
                            number = queue(number)    #排序是为了下次循环能正确遍历下一个数
                    break #得到一次结果为20的就终止这次循环,不用再往下遍历了,进行下一次
    

      说一下上面代码的思路吧,直接举例子,所得的n为20,循环就是先取20,对了,这里循环用到的数组需要是排序好的,然后从大到小取。回到循环,取20,这个时候判断结果为20,就将结果保存。只有一个数,就不用调用GNN()(下一个函数,等会讲)了;下次循环为先取19,19<20,将19保存下来,然后一直遍历,直到取1的时候结果为20为止。先把[19,1]保存,然后调用GNN(),传入的参数为数组[19]和1。这是主要思路,语句的注释代码里写了。FNN()函数主要目的就是为了找出类似[20],[19,1],[18,2],[17,3],[16,4]这种大的情况,并且不要管大的19,18这些数的拆分,没有嵌套调用函数,拆分的工作就交给GNN()。

    def GNN(number, n_small): #二次遍历嵌套,传进来的是没有n_small的数组和要求的n_small
        for i in range(len(num)-1, -1, -1): #后序遍历
            plus = 0
            number_small = [] #用来求n的和的数组
            for j in range(i, -1, -1): #前面几句和FNN一样
                #  下面的大判断,第一个判断不用说,第二个是为了防止重复number中的值,比如[6,5,4,3,2],而5能分成3,2,但是传进来的 
                #number[6,4,3,2]已经有了,此时就不能再分了第三个条件是避免FNN传进来的n_small和循环的num[j]一样而耽误时间
                if plus + num[j] < n_small and num[j] not in number and num[j] != n_small:
                    number_small.append(num[j])
                    plus = plus + num[j]
                elif plus + num[j] == n_small and num[j] not in number and num[j] != n_small: #同上
                    number_small.append(num[j])
                    NumberPlus = queue(number + number_small) #这个就是没有n_small的数组加上n_small求和的数组
                    if NumberPlus not in ReNumber:  #避免重复
                        #print("副要:%s" % NumberPlus)
                        ReNumber.append(NumberPlus)
                    ###下面的类似FNN###
                    if len(number_small) > 1:
                        for k in range(0, len(number_small)): #这个是从0开始
                            temp = number_small[k]
                            number_small.remove(temp)
                            GNN(queue(number + number_small), temp) #里面的排序可以不用,下次嵌套会排序,这里用是为了保险
                            number_small.append(temp)
                            number_small = queue(number_small)
                    break
    

      GNN是进行拆分的,接着上文讲,传进来的参数是[19]和1,1就是要进行拆分的,但是因为1没有能拆的情况,就退出了。举一个多点的例子,比如[9,8,3]这是FNN()最开始找到的,然后FNN()向GNN()传入[9,3]和8,之后GNN()对3进行拆分,同样的循环,之后分成了[7,1]存到number_small,当然分的时候也要判断7和1在刚刚的[9,3]里面有没有,没有才能分。之后将[9,3]+[7,1]存到ReNumber,程序里面的副要就是为了观察这个输出的,然后再判断[7,1]的情况,7能再分成[5,2],然后出循环。然后再找8的拆分情况[6, 2],类推下去,重复的就会省去。循环结束。再回到FNN(),刚刚的8用完了,轮到3了,分成[2,1],输出[9,8,2,1]。这样一直下去,整个结果就出来了。

    def QuickSort(str1): #从小到大,并把负数调到列表最后
        for i in range(0, len(str1)):
            for j in range(0, i):
                if str1[j] > str1[i]:
                    t = str1[j]
                    str1[j] = str1[i]
                    str1[i] = t
        for i in range(len(str1)-1, -1, -1):
            if str1[i] < 0:
                t = str1[i]
                str1.remove(t)
                str1.append(t)
        return str1
    

      这个函数说下后面的把负数调到最后是为什么吧,比如输入[1,-1,-2,2,-3,3,4],正常排序后结果是[-3,-2,-1,1,2,3,4],但是我这个程序是后续遍历的,那这样永远也轮不到负数兄弟出场了,所以得将他们弄到最后面,让他们一开始就进入计算,相当于把要求的n变大了。其他的代码比较简单我就不说啦!完整代码如下:

    #!/usr/bin/env python
    # -*- coding:utf-8 -*-
    # Author:Inori
    
    def WriteFile(file1): #写文件
        f = open("plus.txt", "a") #这里写文件要用a,因为每次循环输出到文件不能清空文件
        f.write(str(file1))
        f.write('
    ')
        f.close()
        
    def QuickSort(str1): #从小到大,并把负数调到列表最后
        for i in range(0, len(str1)):
            for j in range(0, i):
                if str1[j] > str1[i]:
                    t = str1[j]
                    str1[j] = str1[i]
                    str1[i] = t
        for i in range(len(str1)-1, -1, -1):
            if str1[i] < 0:
                t = str1[i]
                str1.remove(t)
                str1.append(t)
        return str1
    
    def queue(str1): #将列表数据从大到小排序
        for i in range(0, len(str1)):
            for j in range(0, i):
                if str1[j] < str1[i]:
                    t = str1[j]
                    str1[j] = str1[i]
                    str1[i] = t
        return str1
    
    def GNN(number, n_small): #二次遍历嵌套,传进来的是没有n_small的数组和要求的n_small
        for i in range(len(num)-1, -1, -1): #后序遍历
            plus = 0
            number_small = [] #用来求n的和的数组
            for j in range(i, -1, -1): #前面几句和FNN一样
                #   下面的大判断,第一个判断不用说,第二个是为了防止重复number中的值,比如number中有1,而4能分成3,1,此时就不能这样分
                #第三个条件是避免FNN传进来的某个不能分的数而耽误时间,[19,1]传进来的n_small=1,就不用再分了
                if plus + num[j] < n_small and num[j] not in number and num[j] != n_small:
                    number_small.append(num[j])
                    plus = plus + num[j]
    
                elif plus + num[j] == n_small and num[j] not in number and num[j] != n_small: #同上
                    number_small.append(num[j])
                    NumberPlus = queue(number + number_small) #这个就是没有n_small的数组加上n_small求和的数组
                    if NumberPlus not in ReNumber:  #避免重复
                        #print("副要:%s" % NumberPlus)
                        ReNumber.append(NumberPlus)
    
                    ###下面的类似FNN###
                    if len(number_small) > 1:
                        for k in range(0, len(number_small)):
                            temp = number_small[k]
                            number_small.remove(temp)
                            GNN(queue(number + number_small), temp) #里面的排序可以不用,下次嵌套会排序,这里保险
                            number_small.append(temp)
                            number_small = queue(number_small)
                    break
                    
    def FNN(n): #首次遍历嵌套
        for i in range(len(num)-1, -1, -1): #后序遍历
            number = []     #这个数组是用来存储一次和为n的,比如[n],[n-1,1]
            plus = 0    #这个就是和了,判断和20的关系
            for j in range(i, -1, -1):  #二层循环,
                if plus + num[j] < n:    #累计求和,判断和n的关系
                    number.append(num[j])   #如果小于n,就加入数组num
                    plus = plus + num[j]    #这个时候才把和加到plus里面
    
                elif plus + num[j] == n:    #等于n的情况
                    number.append(num[j])
                    number = queue(number)    #将得到的数组排序成从大到小,作用后面会讲到
    
                    if number not in ReNumber:  #避免重复
                        ReNumber.append(number)     #将得到的一种情况存储起来
                    #print("主要:%s" % number)    #可以不用,但是可以让人很清楚的看结果
    
                    if len(number) > 1:     #防止[20]这个情况也会进入循环
                        for k in range(1, len(number)):     #开始调用另一个函数,这里不能取第一个数,所以从1开始
                            t = number[k]      #把number[k]先转给t,不然下面的remove就把这个数给删了
                            number.remove(t)      #把number[k]先去了
                            GNN(number, t)    #此时传进去的就是没有number[k]的数组,因为我们要求t的和的情况
                            number.append(t)      #把删除的加回去
                            number = queue(number)    #排序是为了下次循环能正确遍历下一个数
                    break #得到一次结果为20的就终止这次循环,不用再往下遍历了,进行下一次
                    
    if __name__ == '__main__':
        num = []
        ###下面两句是用于自己输入数组的,用空格隔开
        print("请输入数组:")
        # 下面一步是利用map将输入的[1 2 3 4]分成[1,2,3,4],但是出来的是map类型,
        # 所以要转成list型,float是把里面的数按那种类型分,可以是int,这里是由于可以输入浮点型
        num = list(map(float, input().split()))
        n = float(input('请输入:'))
        ReNumber = [] #存储已经求和的数组
        
        f = open("plus.txt", "w") #清空文本,前面函数用的a,这次w,作用就是第二次运行代码清空文件夹
        f.close()
        
        QuickSort(num) #排序
        FNN(n)
        queue(ReNumber)
        for i in ReNumber: #最后将结果写文件
            WriteFile(i)
            print(i)
    

    转载请标明出处,谢谢!

  • 相关阅读:
    POJ 1936 All in All
    Blue Jeans POJ 3080 寻找多个串的最长相同子串
    Spell checker POJ 1035 字符串
    密码锁
    luogu P1083 借教室
    BZOJ 1588: [HNOI2002]营业额统计
    BZOJ 1433: [ZJOI2009]假期的宿舍
    luogu P1231 教辅的组成
    luogu P2756 飞行员配对方案问题
    luogu P3386 【模板】二分图匹配
  • 原文地址:https://www.cnblogs.com/hecong/p/13181675.html
Copyright © 2011-2022 走看看