深搜之少林神棍问题

问题

要用给出的木棒凑成等长的棍子,求能凑成的棍子的最小长度。输入第一行为木棒总数,第二行为每个木棒的长度。输出为凑成的棍子的最小长度

解题思路

枚举所有可能的棍子长度。从最长的那根木棒的长度一直枚举到木棒长度总和的一半,对每个假设的棍子长度,试试看能否拼齐若干根棍子。全部枚举需要耗费大量的时间空间,通过”剪枝”缩小解空间,共有四种剪枝方案,详细见代码注释

输入

9
5 2 1 5 2 1 5 2 1
4
1 2 3 4

输出

6
5

代码如下

public class Main {

    static int LastNumber = 0;
    static int L;                                              //棍子的长度
    static int N;                                              //木棒的数量
    static Vector<Integer>anLength = new Vector<Integer>();    //每根木棒的长度
    static int[]anUsed;                                        //标记木棒是否被用过

    public static void main(String[] args) {

        Scanner in = new Scanner(System.in);
        N = in.nextInt();
        if(N==0)return;
        anUsed = new int[N];
        int totalLength=0;
        for(int i=0;i<N;i++) {
            anLength.add(in.nextInt());
            totalLength+=anLength.get(i);
        }
        //对木棒的长度进行从大到小的排序
        Collections.sort(anLength,Collections.reverseOrder());
        for(L=anLength.get(0);L<=totalLength/2;L++) {
            //能整除才能用上全部木棒
            if(totalLength%L!=0)continue;
            //Dfs(R,M) R还没被用掉的木棒数量,M当前正在拼的棍子还缺少的长度
            //初始状态:(N,L)
            //终止状态:(0,0)
            if(Dfs(N,L)) {
                System.out.println(L);break;
            }
        }
        //搜索不到组成2根及2根以上棍子的方法,则所有木棒只能组成一根棍子
        if(L>totalLength/2)System.out.println(totalLength);
        in.close();
    }

    private static boolean Dfs(int R, int M) {

        if(R==0 && M==0)
            return true;
        if(M==0)   //一根刚刚拼完
            M = L; //开始拼新的一根

        int start = 0;
        if(M != L)start = LastNumber+1;  //剪枝4:多根木棒组成一根木棍,让木棒按长度由大到小排列组成,
        //不必搜索当前木棒比上一根木棒长的情况,因为如果此种情况可以的话只是相当于将组成情况不按长度大小
        //排序,不按长度大小排序可成功那么按长度大小排序也可成功eg:3 2不成功,换成2 3也不会成功
        for(int i=start;i<N;i++) {

            if(anUsed[i]==0 && anLength.get(i) <=M) {

                if(i>0) {
                    if(anUsed[i-1]==0 && anLength.get(i-1)==anLength.get(i))
                        continue;
                }//剪枝1:上次没有拼接成功,这次不尝试长度相同的木棒
                anUsed[i] = 1;LastNumber = i;
                if(Dfs(R-1, M-anLength.get(i)))
                    return true;
                else {
                    anUsed[i] = 0;//说明本次不能用第i根,第i根以后还有用
                    if(anLength.get(i) == M ||M==L)//剩余长度为L,说明正在拼第一根木棒
                        return false;//剪枝3:如果当前位置不行,且为最后一根木棒,不需要继续往下试              
                    //剪枝2:如果为第一根木棒的情况没拼成功,则这跟木棒永远也不可能被用上
                    //第一根木棒m,如果在下一次能够拼成,则在这次也一定能够拼成,矛盾。所以,需要更换L的值,返回false
                }
            }
        }

        return false;
    }
}

猜你喜欢

转载自blog.csdn.net/l903445981/article/details/80157107
今日推荐