問題描述
喬治拿來一組等長的木棒,將它們隨機地砍斷,使得每一節木棍的長度都不超過50個長度單位。然后他又想把這些木棍恢復到為裁截前的狀態,但忘記了初始時有多少木棒以及木棒的初始長度。請你設計一個程序,幫助喬治計算木棒的可能最小長度。每一節木棍的長度都用大於零的整數表示。
輸入格式
輸入包含多組數據,每組數據包括兩行。第一行是一個不超過64的整數,表示砍斷之后共有多少節木棍。第二行是截斷以后,所得到的各節木棍的長度。在最后一組數據之后,是一個零。
輸出格式
為每組數據,分別輸出原始木棒的可能最小長度,每組數據占一行。
樣例輸入
9
5 2 1 5 2 1 5 2 1
4
1 2 3 4
0
樣例輸出
5
6
算法思路
通過問題描述可以初步得知,木棒的最小可能長度至少要大於木棒長度序列中的最大值且一定能被木棒長度序列之和整除。因此我們就可以我們就可以在這兩個上下界之間,通過深度搜索的方式不斷的試探每一個可能的最小長度,若試探成功則該長度即為木棒的最小可能長度,若失敗則進行下一個可能的長度進行試探,直至成功。算法參考於這位大佬
程序如下
import java.util.Scanner;
public class Main{
public static int g;//每組拼接完畢后,所拼接好的木棒數
public static int len;//每組滿足要求的拼接后的木棒的長度,每組木棒總長度 = g * len;
/*
深度搜索
a:每組木棒的長度數組
visited:為數組中每一個木棒長度設置一個訪問標志,默認初值為0
n:表示每組木棒數組的長度
nowlen:表示當前已拼接成的木棒的長度
nowget:表示現在拼接成的木棒的總數,當nowGet = g時找到符合要求的木棒長
cnt:拼接過程中查找剩下符合要求的木棒應該從哪個下標開始查找
*/
public static boolean dfs(int[] a,int[] visited,int n,int nowlen,int nowget,int cnt){
if(cnt >= n) return false; //下標超過木棒總數n時,顯然不滿足
if(nowget == g) return true;//當前長度下獲取的總個數與g相等說明符合條件
//開始遍歷查找
for (int i = cnt; i < n; i++) {
//訪問沒有被用過的木棒
if(visited[i] == 0){
//當加入當前的木棒后,恰好能拼接成需要的長度時
if(nowlen + a[i] == len){
visited[i] = 1;//標記當前木棒已被使用
//在此組滿足后,進行下一組的尋找,此時下一組的nowlen=0,nowget=nowget+1;
if(dfs(a,visited,n,0,nowget+1,nowget)){
//遞歸,直到最后一組都滿足需要的長度時說明查找成功
return true;
}
//查找失敗,說明此木棒不可選
visited[i] = 0;//將visited重新置為0,進行回溯
return false;
}
//當找到的一組木棒還小於拼接成需要的長度時
if(nowlen + a[i] < len){
visited[i] = 1;//標記當前木棒已被使用
//接着遞歸,查找下一個,即從i的下一個i+1開始
if(dfs(a,visited,n,nowlen+a[i],nowget,i+1)){
//在當前滿足的基礎上,依次向后遞歸
return true;//查找成功
}
//查找失敗
visited[i] = 0;//回溯
//剪枝,在當前搜索時,前面長度為0,並且第一根沒有被成功使用,說明第一跟始終要被放棄的,所以這種組合不會成功
if(nowlen == 0) return false;
//如果有一根木棒加上去已經知道不滿足要求,則與它同長度的木棒都可以跳過
for ( ;a[i] == a[i+1] && i + 1 < n;i++);
}
}
}
return false;
}
//冒泡排序,順序:由大到小
public static void sort(int[] arr,int num){
int temp;
for (int i = 0; i < num-1; i++) {
for (int j = i+1; j < num; j++) {
if(arr[i] < arr[j]){
temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
}
}
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
int[][] maxti = new int[100][100];//用於存儲輸入每組木棒的長度
int[] sumArr = new int[100];//用於存儲每組木棒的總長度
int[] eachCounts = new int[100];//用於存儲每組木棒的數量
int[] a = new int[100];//用與存儲每組木棒的長度序列
int n;//每組木棒總數
//用戶輸入每組的木棒數
int inc = 0;//設置自增變量,便於去二維數組maxti中取值
int sum;//用於計算每組木棒總長的中間變量
while(true){
n = input.nextInt();//每組木棒的總個數
eachCounts[inc] = n;
if(n == 0){//輸入為0時,結束輸入
break;
}
sum = 0;
for (int i = 0; i < n; i++) {
maxti[inc][i] = input.nextInt();//二維數組maxti的每一行表示一組數據
sum += maxti[inc][i];
}
sumArr[inc] = sum;//存儲每一組數據的總和
inc++;
}
//對輸入的每組木棒序列進行遞歸搜索
for (int i = 0; i < inc; i++) {//對二維數組一行一行訪問
int[] visited = new int[100];//設置每組木棒的訪問標志量
for (int j = 0; j < eachCounts[i]; j++) {
a[j] = maxti[i][j];
}
//a = maxti[i];
sort(a,eachCounts[i]);//從大到小排序得到的序列,a[0]顯然是序列中的最大值
for (len = a[0];len <= sumArr[i];len++) {//對原木棒可能的長度進行遍歷查找
if(sumArr[i] % len != 0) continue;//有題意可知,最后選的木棒長度一定是能被總木棒長度整除的
g = sumArr[i] / len;//拼接后的木棒數
//剪枝,滿足要求退出循環
if(dfs(a,visited,eachCounts[i],0,0,0)){
break;
}
}
//輸出滿足要求得木棒長
System.out.println(len);
}
}
}
