最小木棒问题 dfs+ 减枝

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>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值