• Sticks


    Problem Description
    George took sticks of the same length and cut them randomly until all parts became at most 50 units long. Now he wants to return sticks to the original state, but he forgot how many sticks he had originally and how long they were originally. Please help him and design a program which computes the smallest possible original length of those sticks. All lengths expressed in units are integers greater than zero. 
    Input
    The input contains blocks of 2 lines. The first line contains the number of sticks parts after cutting, there are at most 64 sticks. The second line contains the lengths of those parts separated by the space. The last line of the file contains zero.
    Output
    The output file contains the smallest possible length of original sticks, one per line. 
    Sample Input
    9
    5 2 1 5 2 1 5 2 1
    4
    1 2 3 4
    0
    Sample Output
    6
    5

    这道题目,我的思路是,既然要求最小的长度,那么就可以从min-max开始遍历,那么当遇到第一个符合要求的,即为解:

    1)相对来说,最大值肯定不是无穷大,而是各根棍子的长度之和,因此我们可以在输入棍子的长度的时候,建立一个变量total,用以记录总长度;同样,能够符合题目条件的最小值未必是1,比如说,有4根棍子,长度分别是1,5,1,5,那么1这个长度是绝对不符合的,至少也应该是5,即各根棍子的最大值,所以也应该建立一个变量maxLen, 用以记录长度的最大值。第一步,我们可以把遍历的范围缩减到 maxLen~total;

    2)由于组成的棍子必须是整数,所以假设tarLen是可能符合题目的值,那么如果不满足total % tarLen == 0的,统统可以排除,所以,遍历范围可以进一步缩减为maxLen~total/2;

    初次代码尝试如下:

    import java.util.Scanner;
    
    public class solution {
        public static void main(String[] args) {
            Scanner in = new Scanner(System.in);
            int N = in.nextInt();
            while(N != 0) {
                int[] lens = new int[N];
                int max = 0;
                int total = 0;
                for(int i = 0; i < N; i++) {
                    lens[i] = in.nextInt();
                    total += lens[i];
                    max = max > lens[i]?max : lens[i];
                }
                int result = findShortestLength(lens, N, total, max);
                System.out.println(result);
                N = in.nextInt();
            }
        }
        
        public static int findShortestLength(int[] lens, int N, int total, int max) {
            for(int tarLen = max; tarLen <= total/2; tarLen++) {
                boolean[] visited = new boolean[N+1];
                if(total % tarLen == 0) {
                    //int num = total / tarLen;
                    if(tryToFindLen(lens, visited, N, N, 0, tarLen)) {
                        return tarLen;
                    }
                }    
            }
            return total;
        }
        
        public static boolean tryToFindLen(int[] lens, boolean[] visited, int left, int N, int curLen, int tarLen) {
            if(left == 0) {
                if(curLen == 0) {
                    return true;
                }
            }else {
                for(int idx = 0; idx < N; idx++) {
                    if(!visited[idx]) {
                        visited[idx] = true;
                        boolean flag = false;
                        if(curLen+lens[idx] < tarLen) {
                            flag = tryToFindLen(lens, visited, left-1, N, curLen+lens[idx], tarLen);
                        }else if(curLen+lens[idx] == tarLen) {
                            flag = tryToFindLen(lens, visited, left-1, N, 0, tarLen);
                        }else {
                            flag = false;
                        }
                        visited[idx] = false;
                        if(flag) {
                            return flag;
                        }
                    }
                }
            }
            return false;
        }
    }

    这次尝试,理论上来说是没有问题的(疯狂点头),毕竟在编译器上面跑的时候,是可以得出正确答案的(当然只有样例的那两个),但是效率太低了。这次的尝试,首先是以还有没有剩余的木棍座位终止递归的条件;其次,对应每个tarLen,都要遍历一遍所有的木棍,所以这个算法的效率大概为O(N*N)?(只是针对tryToFindLen这个函数)。稍微优化一下:

    1)由于给出的木棍长度,有可能是重复的,所以我们大概不必去访问每根棍子,转而去遍历每个长度即可,这样就可以省了访问重复长度的时间;

    2)由于组合的木棍,必须是整数,所以除了每根棍子的长度都一样这种特殊情况外,组合而成的棍子,范围在2~total/maxLen,如果以组合的棍子数作为终止递归的条件,会不会好一点(想了一下,其实这里并不算优化点,因为进入到下一层时,这个数并不一定会+1)

    第二次尝试代码:

    import java.util.Scanner;
    
    public class solution {
        public static void main(String[] args) {
            Scanner in = new Scanner(System.in);
            int N = in.nextInt();
            while(N != 0) {
                int[] lens = new int[N];
                int[] counts = new int[51];
                int max = 0;
                int total = 0;
                for(int i = 0; i < N; i++) {
                    lens[i] = in.nextInt();
                    total += lens[i];
                    counts[lens[i]]++;
                    max = max > lens[i]?max : lens[i];
                }
                int result = findShortestLength(counts, total, max);
                System.out.println(result);
                N = in.nextInt();
            }
        }
        
        public static int findShortestLength(int[] counts, int total, int max) {
            for(int tarLen = max; tarLen <= total/2; tarLen++) {
                if(total % tarLen == 0) {
                    int num = total / tarLen;
                    if(tryToFindLen(counts, 0, num, max, 0, tarLen)) {
                        return tarLen;
                    }
                }    
            }
            return total;
        }
        
        public static boolean tryToFindLen(int[] counts, int sticks, int num, int max, int curLen, int tarLen) {
            if(sticks == num) {
                return true;
            }
            if(curLen > tarLen) {
                return false;
            }else if(curLen == tarLen) {
                return tryToFindLen(counts, sticks+1, num, max, 0, tarLen);
            }else {
                for(int len = max; len > 0; len--) {
                    if(counts[len] > 0) {
                        counts[len]--;
                        boolean flag = tryToFindLen(counts, sticks, num, max, curLen+len, tarLen);
                        counts[len]++;
                        if(flag) {
                            return true;
                        }
                        
                    }
                }
                return false;
            }
        }
            
    }

    这个还有一处地方可优化的,就是,在传入当前已经组合完成的长度时,如果当前长度仍小于目标长度,也不需要每次都从max开始遍历,比如说,目标长度为8,当前长度为6,最大长度为5,那么5肯定不符合的,因为若想要完成目标长度,只需要8-6=2即可,所以只需在长度<=2的范围内继续尝试;另一种情况,如果目标长度为12,当前长度为6,最大长度为5,那么12-6>5,仍需要在最大长度处开始尝试,所以这里需要一个判断;第三种情况,当前长度为0,那么肯定是从最大长度处开始尝试,

    第三次代码尝试:(再不通过我就放弃)

    import java.util.Scanner;
    
    public class solution {
        public static void main(String[] args) {
            Scanner in = new Scanner(System.in);
            int N = in.nextInt();
            while(N != 0) {
                int[] lens = new int[N];
                int[] counts = new int[51];
                int max = 0;
                int total = 0;
                for(int i = 0; i < N; i++) {
                    lens[i] = in.nextInt();
                    total += lens[i];
                    counts[lens[i]]++;
                    max = max > lens[i]?max : lens[i];
                }
                int result = findShortestLength(counts, total, max);
                System.out.println(result);
                N = in.nextInt();
            }
        }
        
        public static int findShortestLength(int[] counts, int total, int max) {
            for(int tarLen = max; tarLen <= total/2; tarLen++) {
                if(total % tarLen == 0) {
                    int num = total / tarLen;
                    if(tryToFindLen(counts, 0, num, max, 0, tarLen)) {
                        return tarLen;
                    }
                }    
            }
            return total;
        }
        
        public static boolean tryToFindLen(int[] counts, int sticks, int num, int max, int curLen, int tarLen) {
            if(sticks == num) {
                return true;
            }
            if(curLen > tarLen) {
                return false;
            }else if(curLen == tarLen) {
                return tryToFindLen(counts, sticks+1, num, max, 0, tarLen);
            }else {
                if(curLen == 0) {
                    for(int len = max; len > 0; len--) {
                        if(counts[len] > 0) {
                            counts[len]--;
                            boolean flag = tryToFindLen(counts, sticks, num, max, len, tarLen);
                            counts[len]++;
                            return flag;
                        }
                    }
                    return false;
                }else {
                    int len = max < tarLen-curLen ? max:tarLen-curLen;
                    for( ; len > 0; len--) {
                        if(counts[len] > 0) {
                            counts[len]--;
                            boolean flag = tryToFindLen(counts, sticks, num, max, curLen+len, tarLen);
                            counts[len]++;
                            if(flag) {
                                return flag;
                            }
                            
                        }
                    }
                    return false;
                }
            }
        }
    }

     貌似通过了。。。不枉我一番苦心LOL

  • 相关阅读:
    Java 获取本机IP
    IDEA2017.3.1破解激活
    java访问https绕过证书信任
    windows版nginx+ftp实现图片服务器的搭建
    json转字符串,json转list,json转pojo的工具类
    文件上传到ftp服务工具类
    一个servlet处理多个功能
    一二级栏目的查询
    后台接收URL地址的参数
    SSH邮箱验证与激活
  • 原文地址:https://www.cnblogs.com/WakingShaw/p/12775403.html
Copyright © 2020-2023  润新知