poj 1011 Java: sticks

題目描述:http://poj.org/problem?id=1011

本題考察的內容是深度優先遍歷(DFS),要實現其要求的輸出並不困難。但是題目存在時間限制,僅僅使用DFS結果會超時。因此需要我們添加一些判斷拼湊成功和不成功的條件,也就是所謂的“剪枝”。

我在“剪枝”過程中遇到困難,查閱網絡資料後解決了問題。在這過程中,很大程度借鑑了這篇博客的內容,也可以看做是對其的解讀。


首先我們來看一下最開始在沒有“剪枝”的條件下的代碼:

<span style="font-size:10px;">import java.util.*;
public class Poj1011_uncut {
	
    static boolean[] used;
    static int num;//分割後木棍的數量
    static int[] s;
    static int sum;
    static int max;
    static int parts;

    //"木棒"表示合成後的stick,"木棍"表示未合成的stick
	public static void main(String[] args) {
		Scanner sc = new Scanner(System.in);
		while((num = sc.nextInt()) != 0){
			used = new boolean[num];
			sum = 0;
			s = new int[num];
			for(int i = 0; i<num; i++){
				s[i] = sc.nextInt();
				sum += s[i];
			}
			
			Arrays.sort(s);//對木棍進行排序(從小到大)
			max = s[num - 1];
			
			//木棒的長度一定大於等於最長的木棍長度,所以從最長的木棍開始
			for(; max<= sum; max++){
				//木棒的長度一定能被總長度整除
				if(sum%max == 0){
					parts = sum/max;//木棒的數目
					if(search(0, num - 1, 0)){
						System.out.println(max);
						break;
					}
				}
			}
		}
		sc.close();
	}
	
	/*
	 * 搜索能拼成一個木棒的木棍
	 * @param res:當前這根木棒已有的長度
	 * @param next:下一個搜索的木棍的下標
	 * @param cpl:已經拼成的木棒數量
	 */
	public static boolean search(int res, int next, int cpl){
		//當res=max時,本次合成成功。
		if(res == max){
			cpl++;
			res = 0;
			next = num - 2;
		}
		
		//cpl = parts,當前所有木棒合成完畢
		if(cpl == parts){
			return true;
		}
		
		//沒有成功,繼續合成
		while(next >= 0){
			//如果當前木棍沒有被用過
			if(used[next] == false){
				//木棒的當前長度+當前搜索的木棍長度 沒有超過max,則可以放入
				if(res + s[next] <= max){
					used[next] = true;
					//繼續搜索成功
					if(search(res + s[next], next - 1, cpl)){
						return true;
					}
					
					//搜索不成功
					used[next] = false;
				}
			}
			next--;
		}
		return false;
	}
}</span><span style="font-size: 14px;">
</span>


這樣的代碼是無法符合運行時間要求的。那麼我們接下來看看,有哪些條件是我們可以用來“剪枝”的。

第一類條件是正確解的充分條件:

1、木棒的長度一定大於等於最長的一根木棍的長度。

2、木棒的長度,一定是總長度的約數。
可以發現其實在上面爲通過的代碼中,也已經使用了這兩個條件。

第二類條件是非正確解的必要條件:

1、若某次搜索中,當前最長的木棍匹配失敗,這當前木棒的長度一定是非正確解。

2、若某次搜索中,當前木棍拼湊成功,但是剩下的無法拼湊成功,則此次匹配也一定失敗。

3、若當前剩餘未使用的木棍加起來都無法小於木棒的長度,則此次匹配也一定失敗。

還有第三類條件,可以使匹配更加高效:

1、如果某一個木棍匹配失敗,則在當前條件下,與其相同長度的木棍都不用在匹配了。

有了以上的條件,我們就可以對之前的代碼進行“剪枝”處理:


import java.util.*;
public class Poj1011_cut {
	
	static boolean[] used;
    static int num;//分割後木棍的數量
    static int[] s;
    static int sum;
    static int max;
    static int parts;

    //"木棒"表示合成後的stick,"木棍"表示未合成的stick
	public static void main(String[] args) {
		Scanner sc = new Scanner(System.in);
		while((num = sc.nextInt()) != 0){
			used = new boolean[num];
			sum = 0;
			s = new int[num];
			for(int i = 0; i<num; i++){
				s[i] = sc.nextInt();
				sum += s[i];
			}
			
			Arrays.sort(s);//對木棍進行排序(從小到大)
			max = s[num - 1];
			
			//木棒的長度一定大於等於最長的木棍長度,所以從最長的木棍開始
			for(; max<= sum; max++){
				//木棒的長度一定能被總長度整除
				if(sum%max == 0){
					parts = sum/max;//木棒的數目
					if(search(0, num - 1, 0)){
						System.out.println(max);
						break;
					}
				}
			}
		}
		sc.close();
	}
	
	/*
	 * 搜索能拼成一個木棒的木棍
	 * @param res:當前這根木棒已有的長度
	 * @param next:下一個搜索的木棍的下標
	 * @param cpl:已經拼成的木棒數量
	 */

	public static boolean search(int res, int next, int cpl){
		//當res=max時,本次合成成功。
		if(res == max){
			cpl++;
			res = 0;
			next = num - 1;
			//當一次合成成功後,next可能指在數組的任意位置,此時應該將其置回,繼續從當前最大的開始合成
		}
		
		//cpl = parts,當前所有木棒合成完畢
		if(cpl == parts){
			return true;
		}
		
		//沒有成功,繼續合成
		while(next >= 0){
			//如果當前木棍沒有被用過
			if(used[next] == false){
				//木棒的當前長度+當前搜索的木棍長度 沒有超過max,則可以放入
				if(res + s[next] <= max){
					used[next] = true;
					//繼續搜索成功
					if(search(res + s[next], next - 1, cpl)){
						return true;
					}
					
					//搜索不成功
					used[next] = false;
					
					//若本次搜索失敗時,res=0,說明當前最長木棍無法合成木棒,則肯定會搜索失敗
					if(res == 0){
						break;
					}
					
					//可以合成當前的,但是剩下的無法合成
					if(res + s[next] == max){
						break;
					}
				}
				
				//如果某一個木棍匹配失敗,則在當前條件下,與其相同長度的木棍都不用在匹配了
				int i = next - 1;
				while(i >=0 && s[i] == s[next]){
					i--;
				}
				next = i;
				
				//計算剩餘木棍的總長度
				int l_s = 0;
				while(i >= 0){
					if(used[i] == false){
						l_s += s[i];
					}
					i--;
				}
				//如果剩餘木棍的總長度都小於max-res,則拼湊一定失敗
				if(l_s < max - res){
					break;
				}
				continue;
			}
			next--;//此處用於控制當if(used[next] == false)不滿足時,指向下一個木棍
		}
		return false;
	}
}



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章