Description
乔治拿来一组等长的木棒,将它们随机地砍断,使得每一节木棍的长度都不超过50个长度单位。然后他又想把这些木棍恢复到为裁截前的状态,但忘记了初始时有多少木棒以及木棒的初始长度。请你设计一个程序,帮助乔治计算木棒的可能最小长度。每一节木棍的长度都用大于零的整数表示。
Input
输入包含多组数据,每组数据包括两行。第一行是一个不超过64的整数,表示砍断之后共有多少节木棍。第二行是截断以后,所得到的各节木棍的长度。在最后一组数据之后,是一个零。
Output
为每组数据,分别输出原始木棒的可能最小长度,每组数据占一行。
Sample Input
9 5 2 1 5 2 1 5 2 1 4 1 2 3 4 0
Sample Output
6 <p>5</p><p> </p><p></p><p style="margin-top: 0px; margin-bottom: 0px; padding-top: 0px; padding-bottom: 0px; color: rgb(85, 85, 85); font-family: "microsoft yahei"; font-size: 15px;">题解:</p><p style="margin-top: 0px; margin-bottom: 0px; padding-top: 0px; padding-bottom: 0px; color: rgb(85, 85, 85); font-family: "microsoft yahei"; font-size: 15px;"></p><p style="margin-top: 0px; margin-bottom: 0px; padding-top: 0px; padding-bottom: 0px; color: rgb(85, 85, 85); font-family: "microsoft yahei"; font-size: 15px;">假设initLen为原始木棍的最短长度,maxLen为折断之后木棍的最大长度,sumLen为所有木棍之和,则必然有</p><p style="margin-top: 0px; margin-bottom: 0px; padding-top: 0px; padding-bottom: 0px; color: rgb(85, 85, 85); font-family: "microsoft yahei"; font-size: 15px;">initLen在【maxLen, sumLen】之间,并且满足sumLen%initLen == 0;</p><p style="margin-top: 0px; margin-bottom: 0px; padding-top: 0px; padding-bottom: 0px; color: rgb(85, 85, 85); font-family: "microsoft yahei"; font-size: 15px;"><span face="Times New Roman"> </span></p><p style="margin-top: 0px; margin-bottom: 0px; padding-top: 0px; padding-bottom: 0px; color: rgb(85, 85, 85); font-family: "microsoft yahei"; font-size: 15px;"><span face="Times New Roman">DFS</span>前先对所有木棍按降序排序</p><p style="margin-top: 0px; margin-bottom: 0px; padding-top: 0px; padding-bottom: 0px; color: rgb(85, 85, 85); font-family: "microsoft yahei"; font-size: 15px;"><span face="Times New Roman"> </span></p><p style="margin-top: 0px; margin-bottom: 0px; padding-top: 0px; padding-bottom: 0px; color: rgb(85, 85, 85); font-family: "microsoft yahei"; font-size: 15px;">剪枝:</p><p style="margin-top: 0px; margin-bottom: 0px; padding-top: 0px; padding-bottom: 0px; color: rgb(85, 85, 85); font-family: "microsoft yahei"; font-size: 15px;"><span face="Times New Roman">1、 </span>由于所有原始棒子等长,那么必有<span face="Times New Roman">sumlen%Initlen==0</span>;</p><p style="margin-top: 0px; margin-bottom: 0px; padding-top: 0px; padding-bottom: 0px; color: rgb(85, 85, 85); font-family: "microsoft yahei"; font-size: 15px;"><span face="Times New Roman">2、 </span>若能在<span face="Times New Roman">[maxlen,sumlen-InitLen]</span>找到最短的<span face="Times New Roman">InitLen</span>,该<span face="Times New Roman">InitLen</span>必也是<span face="Times New Roman">[maxlen,sumlen]</span>的最短;若不能在<span face="Times New Roman">[maxlen,sumlen-InitLen]</span>找到最短的<span face="Times New Roman">InitLen</span>,则必有<span face="Times New Roman">InitLen=sumlen</span>;</p><p style="margin-top: 0px; margin-bottom: 0px; padding-top: 0px; padding-bottom: 0px; color: rgb(85, 85, 85); font-family: "microsoft yahei"; font-size: 15px;"><span face="Times New Roman">3、 </span>由于所有棒子已降序排序,在<span face="Times New Roman">DFS</span>时,若某根棒子不合适,则跳过其后面所有与它等长的棒子;</p><p style="margin-top: 0px; margin-bottom: 0px; padding-top: 0px; padding-bottom: 0px; color: rgb(85, 85, 85); font-family: "microsoft yahei"; font-size: 15px;"><span face="Times New Roman">4、 </span>最重要的剪枝:对于某个目标<span face="Times New Roman">InitLen</span>,在每次构建新的长度为<span face="Times New Roman">InitLen</span>的原始棒时,检查新棒的第一根<span face="Times New Roman">stick[i]</span>,若在搜索完所有<span face="Times New Roman">stick[]</span>后都无法组合,则说明<span face="Times New Roman">stick[i]</span>无法在当前组合方式下组合,不用往下搜索<span face="Times New Roman">(</span>往下搜索会令<span face="Times New Roman">stick[i]</span>被舍弃<span face="Times New Roman">)</span>,直接返回上一层</p><div> </div><div> </div><div> import java.util.Arrays; import java.util.Comparator; import java.util.Scanner; public class Main { private static int n,sum=0,goal=0; private static Integer stick[]; private static boolean visit[]; public static void main(String[] args) { Scanner scanner = new Scanner(System.in); n = scanner.nextInt(); stick = new Integer[n]; visit = new boolean[n]; for (int i = 0; i < n; i++) { stick[i] = scanner.nextInt(); sum += stick[i]; visit[i] = false; } Main m = new Main(); System.out.println(m.solve()); scanner.close(); } /** * now 表示当前木棍的长度 * index 表示开始检测木棍的下表序列 * count 表示已经组合好的木棍个数 * @param now * @param index * @param count * @return */ boolean dfs(int now, int index, int count){ if (goal * count == sum) { //表示原始木棍长度为 goal时,可以组合完成 return true; } for (int i = index; i < n; i++) { /** * 如果第i个木棍已经被访问过了,就继续往后面搜索 * 否则,第i个没有被访问过,如果i-1也没有被访问,并且i和 i-1的棍长一样长,则i直接跳过 */ if (visit[i] || (i!=0 && !visit[i-1] && stick[i] == stick[i-1])) { continue; } if (now + stick[i] == goal) { //表示找到一种等于假设棍长的组合,所以后面count要加1 visit[i] = true; if (dfs(0, 0, count+1)) { //继续找下一种木棍的组合 return true; } visit[i] = false; //再把visit[i] 设为false,这样的话就不需要在solve函数的for循环里重新对visit函数进行初始化了 return false; }else if (now + stick[i] < goal) { //当前木棍的长度now加上stick[i]的长度小于goal,可以组合在一起 visit[i] = true; if (dfs(now+stick[i], i+1, count)) { //组合i之后的木棍长度变成 now+stick[i],继续从第i+1往后搜索,已经组合好的木棍个数任为count return true; } visit[i] = false; if (now == 0) { //当上面if语句为false时,这里会一步步往上回溯,当now==0 表示在剩余的木棍里,没有找到一组新的组合 return false; } //否则就是把第i个木棍剔除掉,继续从i+1开始找一组组合 } } return false; } int solve(){ Arrays.sort(stick, new IntCompDesc()); //设原始木棍长度为所有木棍长度最大值,然后叠加 for(goal = stick[0]; goal < sum; goal++){ if (sum % goal != 0) { continue; } /* for (int i = 0; i < n; i++) { visit[i] = false; }*/ if (dfs(0, 0, 0)) { break; } for (int i = 0; i < n; i++) { System.out.print(visit[i] + " "); } System.out.println(); } return goal; } } //降序排列 class IntCompDesc implements Comparator<Integer>{ @Override public int compare(Integer o1, Integer o2) { if (o1 <= o2) { return 1; } return -1; } } </div><div> </div>