问题
要用给出的木棒凑成等长的棍子,求能凑成的棍子的最小长度。输入第一行为木棒总数,第二行为每个木棒的长度。输出为凑成的棍子的最小长度
解题思路
枚举所有可能的棍子长度。从最长的那根木棒的长度一直枚举到木棒长度总和的一半,对每个假设的棍子长度,试试看能否拼齐若干根棍子。全部枚举需要耗费大量的时间空间,通过”剪枝”缩小解空间,共有四种剪枝方案,详细见代码注释
输入
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;
}
}