zoukankan      html  css  js  c++  java
  • Project Euler 103:Special subset sums: optimum 特殊的子集和:最优解

    Special subset sums: optimum

    Let S(A) represent the sum of elements in set A of size n. We shall call it a special sum set if for any two non-empty disjoint subsets, B and C, the following properties are true:

    1. S(B) ≠ S(C); that is, sums of subsets cannot be equal.
    2. If B contains more elements than C then S(B) > S(C).

    If S(A) is minimised for a given n, we shall call it an optimum special sum set. The first five optimum special sum sets are given below.

    n = 1: {1}
    n = 2: {1, 2}
    n = 3: {2, 3, 4}
    n = 4: {3, 5, 6, 7}
    n = 5: {6, 9, 11, 12, 13}

    It seems that for a given optimum set, A = {a1, a2, … , an}, the next optimum set is of the form B = {b, a1+b, a2+b, … ,an+b}, where b is the “middle” element on the previous row.

    By applying this “rule” we would expect the optimum set for n = 6 to be A = {11, 17, 20, 22, 23, 24}, with S(A) = 117. However, this is not the optimum set, as we have merely applied an algorithm to provide a near optimum set. The optimum set for n = 6 is A = {11, 18, 19, 20, 22, 25}, with S(A) = 115 and corresponding set string: 111819202225.

    Given that A is an optimum special sum set for n = 7, find its set string.

    NOTE: This problem is related to Problem 105 and Problem 106.


    特殊的子集和:最优解

    记S(A)是大小为n的集合A中所有元素的和。若任取A的任意两个非空且不相交的子集B和C都满足下列条件,我们称A是一个特殊的和集:

    1. S(B) ≠ S(C);也就是说,任意子集的和不相同。
    2. 如果B中的元素比C多,则S(B) > S(C)。

    对于给定的n,我们称使得S(A)最小的集合A为最优特殊和集。前5个最优特殊和集如下所示。

    n = 1: {1}
    n = 2: {1, 2}
    n = 3: {2, 3, 4}
    n = 4: {3, 5, 6, 7}
    n = 5: {6, 9, 11, 12, 13}

    似乎对于一个给定的最优特殊和集A = {a1, a2, … , an},下一个最优特殊和集将是B = {b, a1+b, a2+b, … ,an+b}的形式,其中b是集合A“正中间”的元素。

    应用这条“规则”,我们猜测对于n = 6的最优特殊和集将是A = {11, 17, 20, 22, 23, 24},相应的S(A) = 117。然而,事实并非如此,我们的方法仅仅只能找出近似最优特殊和集。对于n = 6,最优特殊和集是A = {11, 18, 19, 20, 22, 25},相应的S(A) = 115,对应的集合数字串是:111819202225。

    若集合A是n = 7时的最优特殊和集,求其对应的集合数字串。

    注意:此题和第105题第106题有关。

    解题

    题目坑了我好久

    注意几点:

    1.上面说的A的子集 B 和C ,B C 只是其中的任意两个子集,不是 B 和C的并等于A

    2.A的任意子集都要满足上面两个条件,同时注意:B和C不能有交集

    3.题目要求的是对于n的最优特殊和集,通过上面调整的是最近特殊和集,最优特殊和集在同样的n的情况下,其集合的和是最小的

    4.7个数不相同,还是升序的

    暴力解题:

    1.在{19,30,37,38,39,41,44} 7个点附近寻找

    2.求出所有的子集

    3.对不相交的子集,利用题目给的两个条件进行求解

    4.和最小的集合就是答案

    下面程序参考了Mathblog,但是其是先求出每个子集的和,对于子集交集的没有看到,我改成求出所有的子集,再暴力找满足条件的值。

    下面程序中在组合成初始集合A,最不好的方法也不是严格的在{19,30,37,38,39,41,44} 附近寻找的。同时,是用ArrayList当作集合用表示不是很好

    JAVA

    package Level4;
    
    import java.io.BufferedReader;
    import java.io.BufferedWriter;
    import java.io.File;
    import java.io.FileNotFoundException;
    import java.io.FileOutputStream;
    import java.io.FileReader;
    import java.io.IOException;
    import java.io.OutputStreamWriter;
    import java.util.ArrayList;
    import java.util.Arrays;
    import java.util.TreeSet;
    
    
    public class PE0103{
        
        // 对{20, 31, 38, 39, 40, 42, 45} //255  中的每个点+-3暴力求出所有的可能 
        // {19,30,37,38,39,41,44} //248
        public static void run(){
            int a[] = {19,30,37,38,39,41,44};
            int min = -3;
            int max = 3;
            int c1[] = {19 ,30, 37, 38, 39, 41,44};
            int c2[] = {19 ,31, 38, 39, 40, 42, 45};
            int c3[] = {20, 31, 38, 39, 40, 42, 45};
            int c4[] = {6, 9, 11, 12, 13};
            int c5[] = {11, 18, 19, 20, 22, 25};
            System.out.println(isOptimum(c1));
            System.out.println(isOptimum(c2));
            System.out.println(isOptimum(c3));
            System.out.println(isOptimum(c4));
            System.out.println(isOptimum(c5));
            for(int a0 = a[0] ;a0<=a[0]+ max ;a0++){
                for(int a1 = a0+1;a1<= a[1]+max ;a1++){
                    for(int a2 = a1+1;a2<=a[2]+max ;a2++){
                        for(int a3 = a2+1;a3<=a[3]+max ;a3++){
                            for(int a4 = a3+1;a4<=a[4]+max ;a4++){
                                for(int a5 = a4+1;a5<=a[5]+max ;a5++){
                                    for(int a6 = a5+1;a6<=a[6]+max ;a6++){
                                        int[] b= {a0,a1,a2,a3,a4,a5,a6};
                                        if(isOptimum(b)){
                                            String str = printArrStr(b);
                                            
                                            str = str + "	 SUM:"+sum(b);
                                            System.out.println(str);
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
    
        }
        public static String printArrStr(int[] a){
            String str ="";
            for(int i=0;i<a.length;i++){
                str +=" "+a[i];
            }
    
            return str;
            
        }
        public static String printArrStr(ArrayList<Integer> list){
            String str ="";
            for(int i=0;i<list.size();i++){
                str += " "+list.get(i);
            }
            return str;
        }
        // 验证 是否是最优子集
        // 子集  一个集合可能有多个子集,而下面的程序只是看成两个自己的情况,两个子集的并集等于原来集合,照成程序有问题
        public static boolean isOptimum(int[] a){
            // 所有的子集
            ArrayList<ArrayList<Integer>> sets = MakeSubsets(a);
            int size = sets.size();
    //        System.out.println(size);
            for(int i=0;i<size;i++){
                ArrayList<Integer> set1 = sets.get(i);
                for(int j=i+1;j<size;j++){
                    ArrayList<Integer> set2 = sets.get(j);
                    if(!isDisjoint(set1,set2)){
                        int sum1 = sum(set1);
                        int sum2 = sum(set2);
                        if(sum1 == sum2)
                            return false;
                        if(set1.size() > set2.size() && sum1 <=sum2)
                            return false;
                        if(set1.size() < set2.size() && sum1 >= sum2)
                            return false;
                    }
                }
            }
            return true;
        }
    
        // 求集合内元素的和
        public static int sum(ArrayList<Integer> set){
            int sum = 0;
            for(int i=0;i< set.size();i++){
                sum += set.get(i);
            }
            return sum;
        }
        public static int sum(int [] a){
            int sum = 0;
            for(int i=0;i< a.length;i++){
                sum += a[i];
            }
            return sum;
        }
        // 两个子集元素是否相交 true 相交 false 不相交 
        public static boolean isDisjoint(ArrayList<Integer> set1,ArrayList<Integer> set2){
            int size1 = set1.size();
            int size2 = set2.size();
            ArrayList<Integer> set = new ArrayList<Integer>();
            for(int i=0;i<size1;i++){
                int element = set1.get(i);
                if(set.contains(element))
                    return true;
                else
                    set.add(element);
            }
            for(int i=0;i<size2;i++){
                int element = set2.get(i);
                if(set.contains(element))
                    return true;
                else
                    set.add(element);
            }
            set.clear();
            return false;
            
        }
        
        // 求出所有子集的和
        public static int[] MakeSubsetSums(int[] a){
            int b[] = new int[(int)Math.pow(2, a.length) ];
            for(int i=1;i<b.length;i++){
                b[i] = MakeSubsetSum(a,i);
            }
            return b;
        }
        // 求出所有的子集
        public static ArrayList<ArrayList<Integer>> MakeSubsets(int a[]){
            ArrayList<ArrayList<Integer>> sets = new ArrayList<ArrayList<Integer>>();
            for(int i=1;i<= (int) Math.pow(2,a.length);i++){
                ArrayList<Integer> set = MakeSubset(a,i);
                sets.add(set);
                String s = printArrStr(set);
    //            System.out.println(s);
            }
            return sets;
                
        }
        // 求出子集
        public static ArrayList<Integer> MakeSubset(int[] a,int m){
            ArrayList<Integer> set = new ArrayList<Integer>();
            for(int i=0;i<a.length ;i++){
                if( m>0 &&(m&1)==1){
                    set.add(a[i]);
                }
                m =m>>1;
            }
            return set;
        }
        // 求子集的和
        // 利用 和  1 进行与运算 并移位
        //  001001 相当于根据 1 所在的位置取 第 2 第 5的位置对应的数
        // &000001
        //----------
        //       1 取出该位置对应的数
        // 下面右移一位后
        //  000100
        // 下面同理了
        public static int MakeSubsetSum(int[] a,int m){
            int sum = 0;
            for(int i=0;i< a.length;i++){
                if( m>0 && (m&1) == 1)
                    sum +=a[i];
                m >>=1;
            }
            return sum;
        }
        public static void main(String[] args){
            long t0 = System.currentTimeMillis();
            run();
            long t1 = System.currentTimeMillis();
            long t = t1 - t0;
            System.out.println("running time="+t/1000+"s"+t%1000+"ms");
        }
    }

    结果

     20 31 38 39 40 42 45     SUM:255
     20 32 39 40 41 43 46     SUM:261
     20 33 40 41 42 44 47     SUM:267
     21 32 39 40 41 43 46     SUM:262
     21 33 40 41 42 44 47     SUM:268
     22 33 40 41 42 44 47     SUM:269
    running time=164s431ms

    第一个就是答案,可以看出,直接根据第6个最优特征子集也可以推出下一个最优特殊子集

    Python 质量也是好差

    # coding=gbk
    
    import time as time 
    import re 
    import math
    import numpy as np 
    def run():
        a= [19,30,37,38,39,41,44]
        f = lambda x:x+1
        a0 = map(f,range(16,22))
        a1 = map(f,range(27,34))
        a2 = map(f,range(34,41))
        a3 = map(f,range(35,42))
        a4 = map(f,range(36,43))
        a5 = map(f,range(37,44))
        a6 = map(f,range(41,48))
        for a in a0:
            for b in a1:
                for c in a2:
                    for d in a3:
                        for e in a4:
                            for f in a5:
                                for g in a6:
                                    if a<b and b<c and c<d and d<e and e<f and f<g:
                                        A = [a,b,c,d,e,f,g]
                                        if isOptimum(A):
                                            print A,'	',sum(A)
        
    # [20, 31, 38, 39, 40, 42, 45]     255
    # [20, 32, 39, 40, 41, 43, 46]     261
    # [20, 33, 40, 41, 42, 44, 47]     267
    # [20, 34, 37, 39, 40, 41, 48]     259
    # [21, 32, 39, 40, 41, 43, 46]     262
    # [21, 33, 40, 41, 42, 44, 47]     268
    # [22, 33, 40, 41, 42, 44, 47]     269
    # running time= 52.3980000019 s
    def isOptimum(a):
        sets = subSets(a)
    #     print len(sets)
        for i in range(len(sets)):
            for j in range(i+1,len(sets)):
                s1 = sets[i]
                s2 = sets[j]
                if isDisjoint(s1,s2) == False:
                    sum1 = sum(s1)
                    sum2 = sum(s2)
                    len1 = len(s1)
                    len2 = len(s2)
    #                 print s1,'	',s2
                    if sum1 == sum2:return False
                    if len1>len2 and sum1<=sum2:return False
                    if len1<len2 and sum1>=sum2:return False
        return True
    def isDisjoint(b,c):
        for bi in b:
            if bi in c:
                return True
        return False
    
    def subSets(a):
        l = 2**len(a)
        sets = []
        for i in range(1,l):
            s = subSet(a,i)
            sets.append(s)
        return sets
    #   求一个子集
    def subSet(a,m):
        s = []
        for i in range(len(a)):
            if (m&1)==1:
                s.append(a[i])
            m =m>>1 
        return s 
        
    t0 = time.time()
    run() 
    t1 = time.time()
    print "running time=",(t1-t0),"s"
    
    
                
  • 相关阅读:
    各版本arm-gcc区别与安装【转】
    内存与文件系统【笔记】
    ramdisk文件系统的介绍与制作【转】
    浅谈linux中的根文件系统(rootfs的原理和介绍)【转】
    mybatis generator自动生成代码
    java no XXX in java.library.path怎么配置
    使用JNotify 监控文件变化
    javaFx中fxml的键盘事件
    okhttp同步异步下载文件,与http请求工具类
    JavaFx2 实现系统托盘SystemTray
  • 原文地址:https://www.cnblogs.com/bbbblog/p/5141383.html
Copyright © 2011-2022 走看看