Sticks
这道题目,我的思路是,既然要求最小的长度,那么就可以从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